Customizing ggplot2 Charts

· 4 min read · Updated March 7, 2026 · intermediate
ggplot2 visualization customization tidyverse

Once you’ve mastered the basics of ggplot2, the real power comes from customization. This tutorial covers how to fine-tune every aspect of your plots—from titles and labels to colors, themes, and legends.

Prerequisites

library(ggplot2)
library(dplyr)

# Sample data for examples
mpg_summary <- mpg |> 
  group_by(manufacturer) |>
  summarise(avg_hwy = mean(hwy), count = n())

Modifying Axis Labels and Titles

The labs() function is your go-to for adding and customizing labels:

ggplot(mpg_summary, aes(x = manufacturer, y = avg_hwy)) +
  geom_col(fill = "steelblue") +
  labs(
    title = "Average Highway MPG by Manufacturer",
    subtitle = "Based on EPA mileage data",
    x = "Car Manufacturer",
    y = "Average Highway MPG",
    caption = "Data: EPA mpg dataset"
  ) +
  theme_minimal()

You can also use ggtitle(), xlab(), and ylab() individually, but labs() is more concise.

Customizing Colors

Continuous Color Scales

For continuous variables, use scale_fill_gradient() or scale_color_gradient():

ggplot(mpg_summary, aes(x = manufacturer, y = avg_hwy, fill = avg_hwy)) +
  geom_col() +
  scale_fill_gradient(
    low = "lightblue",
    high = "darkblue",
    name = "MPG"
  ) +
  theme_minimal()

Discrete Color Scales

For categorical data, use scale_fill_manual() to specify exact colors:

ggplot(mpg_summary, aes(x = manufacturer, y = avg_hwy, fill = manufacturer)) +
  geom_col() +
  scale_fill_manual(
    values = c("audi" = "#E41A1C", "chevrolet" = "#377EB8", 
               "ford" = "#4DAF4A", "honda" = "#984EA3"),
    name = "Manufacturer"
  ) +
  theme_minimal() +
  theme(legend.position = "none")

The viridis scale is excellent for perceptually uniform colors:

ggplot(mpg_summary, aes(x = manufacturer, y = avg_hwy, fill = avg_hwy)) +
  geom_col() +
  scale_fill_viridis_c(option = "plasma", name = "MPG") +
  theme_minimal()

Using Themes

ggplot2 comes with several built-in themes:

# Different theme options
theme_gray()      # Default gray background
theme_bw()        # Black and white
theme_minimal()   # Minimal, no background
theme_classic()   # Classic look with axes
theme_dark()      # Dark background

Apply a theme globally with theme_set():

theme_set(theme_minimal())

Customizing Theme Elements

Use theme() to modify specific elements:

ggplot(mpg_summary, aes(x = manufacturer, y = avg_hwy)) +
  geom_col(fill = "steelblue") +
  labs(title = "Average Highway MPG") +
  theme(
    plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid.major.x = element_blank(),
    legend.position = "top"
  )

Common theme elements:

  • plot.title, plot.subtitle, plot.caption
  • axis.title, axis.text, axis.line
  • legend.position, legend.title, legend.text
  • panel.background, panel.grid

Modifying Legends

Changing Legend Position

ggplot(mpg_summary, aes(x = manufacturer, y = avg_hwy, fill = manufacturer)) +
  geom_col() +
  theme(legend.position = "top")    # "bottom", "left", "right", "none"

Customizing Legend Labels

ggplot(mpg_summary, aes(x = manufacturer, y = avg_hwy, color = manufacturer)) +
  geom_point(size = 3) +
  scale_color_discrete(name = "Car Maker") +
  theme(legend.title = element_text(face = "bold"))

Removing the Legend Altogether

+ theme(legend.position = "none")

Changing Scales

Modifying Axis Limits

# Using coord_cartesian() - preserves the data
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point() +
  coord_cartesian(xlim = c(1, 7), ylim = c(10, 50))

# Using scale functions - actually filters the data
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point() +
  scale_x_continuous(limits = c(1, 7)) +
  scale_y_continuous(limits = c(10, 50))

Customizing Tick Marks

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point() +
  scale_x_continuous(
    breaks = c(2, 4, 6),
    labels = c("2L", "4L", "6L")
  ) +
  scale_y_continuous(
    breaks = seq(10, 50, by = 10)
  )

Faceting

Faceting creates small multiples showing subsets of data:

# Column faceting
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point() +
  facet_wrap(~ class, ncol = 3) +
  theme_minimal()

# Grid faceting
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point() +
  facet_grid(drv ~ cyl) +
  theme_minimal()

Customize facet labels:

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point() +
  facet_wrap(~ class, labeller = label_both) +
  theme(strip.text = element_text(face = "bold"))

Saving Your Plots

Use ggsave() to export plots:

# Save as PNG
ggsave("my_plot.png", width = 8, height = 6, dpi = 300)

# Save as PDF
ggsave("my_plot.pdf", width = 8, height = 6)

# Save with specific dimensions
ggsave("plot.png", width = 10, height = 8, units = "cm")

Summary

Key customization functions:

  • labs() — Add titles, labels, captions
  • scale_fill_*() / scale_color_*() — Customize colors
  • theme() — Modify visual appearance
  • coord_*() — Change coordinate systems
  • facet_*() — Create small multiples
  • ggsave() — Export plots to files

With these tools, you can transform basic ggplot2 visualizations into publication-ready charts that communicate your data effectively.