ggplot2::theme
theme() customizes every non-data visual element in ggplot2. Titles, axis labels, tick marks, panel backgrounds, grid lines, legends, strip text, and plot margins all come from theme settings. The system uses inheritance — parent settings cascade down to child elements unless you override them at a specific level.
Signature
theme(...,
complete = FALSE,
validate = TRUE)
... accepts named arguments for theme elements. complete = TRUE marks the theme as finished, which matters when building reusable theme functions.
Returns: A theme object (a classed list, not a string).
Element Functions
Four functions create themeable elements:
| Function | What it styles |
|---|---|
element_text() | Text — titles, axis labels, legend text |
element_line() | Lines — axis lines, grid lines, tick marks |
element_rect() | Rectangles — panel backgrounds, legend boxes |
element_blank() | Nothing — removes the element entirely |
element_text(family = NULL, face = NULL, colour = NULL,
size = NULL, hjust = NULL, vjust = NULL,
angle = NULL, lineheight = NULL, margin = NULL,
padding = NULL, debug = NULL)
element_line(colour = NULL, linewidth = NULL, linetype = NULL, lineend = NULL)
element_rect(fill = NULL, colour = NULL, linewidth = NULL, linetype = NULL)
Axis Styling
Axis Titles and Text
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
theme(
axis.title = element_text(colour = "navy"),
axis.title.x = element_text(size = 12, face = "bold"),
axis.title.y = element_text(size = 12, face = "bold", angle = 90),
axis.text = element_text(colour = "gray30"),
axis.text.x = element_text(angle = 45, hjust = 1)
)
axis.title.x / axis.title.y target individual axes. Same for axis.text with axis.text.x / axis.text.y.
Axis Lines and Ticks
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
theme(
axis.line = element_line(colour = "black", linewidth = 0.5),
axis.ticks = element_line(colour = "gray40"),
axis.ticks.length = unit(0.1, "inches")
)
Remove ticks entirely:
theme(axis.ticks = element_blank())
Legend Appearance
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point() +
theme(
legend.position = "bottom",
legend.direction = "horizontal",
legend.background = element_rect(fill = "white", colour = NA),
legend.key = element_rect(fill = "gray95", colour = NA),
legend.text = element_text(size = 9),
legend.title = element_text(size = 10, face = "bold"),
legend.margin = margin(t = 0.2, unit = "cm")
)
legend.position accepts "none", "left", "right", "top", "bottom", or a numeric vector for placing inside the plot:
theme(legend.position.inside = c(0.85, 0.15))
Panel Background and Grid
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
theme(
panel.background = element_rect(fill = "white", colour = NA),
panel.border = element_rect(fill = NA, colour = "gray30"),
panel.grid.major = element_line(colour = "gray80", linewidth = 0.3),
panel.grid.minor = element_line(colour = "gray90", linewidth = 0.15),
panel.ontop = FALSE,
plot.background = element_rect(fill = "#f0f0f0", colour = NA)
)
panel.ontop = TRUE draws grid lines on top of your data — useful when points would otherwise obscure the grid.
Plot Titles and Caption Alignment
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
labs(
title = "Engine Size vs Fuel Economy",
subtitle = "1999-2008 vehicles from fueleconomy.gov",
caption = "Data: EPA"
) +
theme(
plot.title = element_text(size = 16, face = "bold", hjust = 0),
plot.subtitle = element_text(size = 11, colour = "gray40", hjust = 0),
plot.caption = element_text(size = 8, colour = "gray50", hjust = 1),
plot.title.position = "panel",
plot.caption.position = "panel",
plot.margin = margin(t = 0.3, r = 0.3, b = 0.3, l = 0.3, unit = "cm")
)
plot.title.position controls whether the title aligns relative to the "panel" (data area) or full "plot" area.
Facet Strip Styling
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(vars(class), ncol = 3) +
theme(
strip.background = element_rect(fill = "steelblue", colour = NA),
strip.text = element_text(colour = "white", size = 10, face = "bold"),
strip.placement = "outside"
)
strip.placement = "inside" puts facet labels next to the axis rather than at the panel edge.
Stripping a Plot Down
The quickest way to a minimal plot:
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
theme(
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank(),
axis.ticks = element_blank(),
axis.line = element_blank(),
panel.border = element_blank(),
panel.background = element_blank()
)
Base Themes Plus Overrides
Built-in complete themes:
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
theme_minimal(base_size = 11) +
theme(panel.grid.minor = element_blank())
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
theme_classic()
Available: theme_grey() / theme_gray(), theme_bw(), theme_linedraw(), theme_light(), theme_dark(), theme_minimal(), theme_classic(), theme_void().
Updating the Active Theme Globally
theme_update() changes settings for all plots until you reset:
old <- theme_update(
panel.background = element_rect(fill = "#fafafa"),
text = element_text(family = "Helvetica", size = 10)
)
# ... make plots ...
theme_set(old) # restore previous theme
theme_get() reads the current theme without changing anything.
How Inheritance Works
Theme elements form a hierarchy. Setting a property high up applies it everywhere below:
text→ all textaxis.title→ all axis titles →axis.title.x→axis.title.x.bottom
Override at the most specific level needed:
theme(
text = element_text(family = "Times"),
axis.title = element_text(colour = "black"),
axis.title.x = element_text(angle = 45)
)
See Also
- /tutorials/r-data-visualization/customizing-ggplot2-themes/ — step-by-step theming walkthrough
- /tutorials/r-data-visualization/ggplot2-basics/ — ggplot2 foundations
- /tutorials/r-data-visualization/ggplot2-facets-and-themes/ — combining facets with theme customization