rguides

ggplot2::scale_color_manual

scale_colour_manual() lets you specify exactly which colour each group in your data gets. Unlike scale_colour_brewer() where you pick a palette and ggplot2 assigns colours, manual scales give you full control — you decide which value gets which colour.

Signature

scale_colour_manual(
  ...,
  values,
  aesthetics = "colour",
  breaks = waiver(),
  na.value = "grey50"
)

Parameters:

  • values — a vector of colours (named or unnamed)
  • aesthetics — which aesthetic(s) to apply to (default "colour")
  • breaks — which factor levels display in the legend
  • na.value — colour for missing values

Returns: Scale object

For scale_fill_manual(), use aesthetics = "fill". Other manual scales: scale_size_manual(), scale_shape_manual(), scale_linetype_manual(), scale_alpha_manual(), scale_linewidth_manual().

Named Vector Mapping

Use a named vector so ggplot2 matches by name, not position:

cols <- c("4" = "#E41A1C", "6" = "#377EB8", "8" = "#4DAF4A")

ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
  geom_point(size = 3) +
  scale_colour_manual(values = cols)

With a named vector, "4" maps to "#E41A1C" regardless of alphabetical ordering. This approach is safer — the mapping stays correct even when some groups are absent from the data.

Unnamed Vector Mapping

Without names, ggplot2 matches by order of factor levels:

ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
  geom_point(size = 3) +
  scale_colour_manual(values = c("red", "blue", "green"))

levels(factor(mtcars$cyl)) is c("4", "6", "8"), so red maps to 4, blue to 6, green to 8. This breaks when any level is absent from the data.

Combining Colour and Fill

Use aesthetics = c("colour", "fill") for both aesthetics at once:

cols <- c("4" = "#E41A1C", "6" = "#377EB8", "8" = "#4DAF4A")

ggplot(mtc3ars, aes(x = mpg, fill = factor(cyl), colour = factor(cyl)) +
  geom_point(shape = 21, size = 3, alpha = 0.6) +
  scale_colour_manual(values = cols, aesthetics = c("colour", "fill"))

This avoids repeating values for each aesthetic.

Custom Legend Labels

Provide labels for the legend:

ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
  geom_point(size = 3) +
  scale_colour_manual(
    values = cols,
    labels = c("Four cylinder", "Six cylinder", "Eight cylinder")
  )

Subsetting the Legend

breaks controls which levels appear in the legend without changing the data. limits controls which data values are included in the scale:

ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
  geom_point(size = 3) +
  scale_colour_manual(
    values = c("4" = "#E41A1C", "6" = "#377EB8"),
    breaks = c("4", "6"),
    labels = c("Four", "Six")
  )

breaks hides "8" from the legend without filtering the data. limits filters the data to include only those values.

Handling Missing Values

Missing values get na.value (default "grey50"):

ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
  geom_point(size = 3) +
  scale_colour_manual(values = cols, na.value = "black")

Programmatic Colour Generation

For many groups, generate colours with scales functions:

n_groups <- length(levels(factor(mtcars$cyl))
pal <- scales::seq_gradient_pal("low" = "#132B43", "high" = "#2C7FB8", space = "Lab")(seq(0, 1, length.out = n_groups)

ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
  geom_point(size = 3) +
  scale_colour_manual(values = pal)

For colourblind-safe palettes across many groups, use viridis or RColorBrewer:

# viridis palette
pal_vir <- viridis::viridis_palette(3, option = "D")
# c("4" = "#440154", "6" = "#21908D", "8" = "#FDE725")

ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
  geom_point(size = 3) +
  scale_colour_manual(values = pal_vir)

Other Manual Scales

The same pattern works for size, shape, linetype, alpha, and linewidth:

ggplot(mtcars, aes(mpg, wt, colour = factor(cyl), linetype = factor(cyl)) +
  geom_line() +
  scale_colour_manual(values = cols) +
  scale_linetype_manual(values = c("solid", "dashed", "dotted"))
ScaleAestheticTypical values
scale_size_manual()sizenumeric values
scale_shape_manual()shape0-25 integer codes
scale_linetype_manual()linetypenames or integers
scale_alpha_manual()alpha0-1 numeric
scale_linewidth_manual()linewidthnumeric

Common Mistakes

Unnamed vectors break when groups are absent from the data. Named vectors are always safer.

Confusing breaks with limits. breaks filters legend entries; limits filters the data. Both commonly misused.

Duplicating values for colour and fill. Use aesthetics = c("colour", "fill") instead.

See Also