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. If you skip the names and pass a plain colour vector instead, ggplot2 assigns colours in the order that the factor levels appear — which means the mapping can shift unexpectedly if your data happens to omit certain groups from a particular subset.

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. The manual scale can control more than just colour — by setting the aesthetics argument to a vector like c("colour", "fill"), you can apply the same colour mapping to both the outline and the interior of points, which saves you from repeating the values vector across two separate scale calls.

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. In addition to controlling the colours themselves, you can customise the text that appears next to each colour swatch in the legend. The labels argument accepts a character vector whose entries replace the raw factor level names, letting you write descriptive, reader-friendly labels without modifying the underlying data.

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")
  )

Beyond changing the legend text, you can also control which factor levels actually appear in the legend by setting the breaks argument. This is useful when you want to draw attention to specific groups without removing them from the plot, or when certain categories are present in the data but are not relevant to the story you are telling.

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. Another practical consideration is how to handle observations that have missing values for the mapped colour variable. By default, scale_colour_manual() assigns a neutral grey to any NA value, but you can change that to any colour you like — or even remove the NA entry from the legend entirely — by adjusting the na.value and na.translate arguments.

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")

Hard-coding colours by hand works well when you have a small number of groups — three cylinder counts, for example. When you are dealing with ten or twenty categories, typing out a long colour vector becomes tedious and error-prone. You can generate the colour values programmatically using functions from the scales package, which let you create smooth colour gradients spanning any number of groups with minimal code.

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. These palettes are designed to be distinguishable by viewers with common forms of colour-vision deficiency. The viridis package in particular provides perceptually uniform colour maps that work well for both continuous and discrete data, and its output can be fed directly into the values argument of a manual scale.

# 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)

The manual-scale pattern is not limited to colour. Every visual aesthetic that can be mapped in ggplot2 has a corresponding manual scale function — you can manually assign specific sizes, shapes, line types, transparency levels, and line widths to each group using the same named-vector or positional approach. This consistency makes the API easy to remember once you have learned it for one aesthetic.

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.

scale_color_manual() applies to the color (outline/line) aesthetic. Use scale_fill_manual() for filled geoms like geom_bar() and geom_boxplot(). The values vector must be named if you want a specific mapping between factor levels and colors; otherwise values are assigned in factor-level order.

See also