ggplot2 element_rect() in R: Style Backgrounds and Borders
The element_rect() function in ggplot2 styles every filled rectangle on a plot, panel backgrounds, plot backgrounds, legend boxes, strip headers, and panel borders. You pass it inside theme() with arguments like fill, color, linewidth, and linetype.
element_rect(fill = "white") # clean white panel element_rect(fill = "grey95") # subtle grey panel element_rect(fill = NA, color = "black") # transparent fill, visible border element_rect(fill = "white", color = "grey80") # white panel with light border element_rect(fill = "#f7f7f7", color = NA) # off-white, no border element_rect(color = "black", linewidth = 0.8) # bold border, default fill element_rect(linetype = "dashed", color = "grey60") # dashed border outline
Need explanation? Read on for examples and pitfalls.
What element_rect() does in ggplot2
element_rect() builds a rectangle-styling specification that ggplot2 plugs into any rectangle slot. It does not draw a rectangle by itself. You always pass the result to theme() as the value of a rectangle slot, like panel.background = element_rect(fill = "white").
Every rectangle element in a ggplot2 plot has a name: panel.background, plot.background, legend.background, legend.box.background, legend.key, strip.background, and panel.border. Each one accepts an element_rect() value. Settings cascade, rect = element_rect(fill = "lightyellow") propagates to every rectangle slot, then specific slots override the cascade.
element_rect() is a specification, not a drawn rectangle. ggplot2 stores the spec, then applies it when rendering. This is why one base theme can repaint every legend box, panel, and strip across 50 plots with a single call.Syntax and arguments
element_rect(fill, color, linewidth, linetype, inherit.blank) covers every rectangle styling need. Each argument has a sensible default, so you only specify what you want to change.
The default theme_grey() draws a grey panel, white gridlines, a transparent plot background, and grey facet strip headers. Every rectangle you see can be restyled with one element_rect() per slot.
| Argument | Type | Common values |
|---|---|---|
fill |
character | "white", "grey95", "#f7f7f7", NA (transparent) |
color (or colour) |
character | "black", "grey80", "#1f77b4", NA |
linewidth |
numeric | 0.25 to 2 (mm); 0.5 is the default |
linetype |
character or integer | "solid", "dashed", "dotted", "longdash" |
inherit.blank |
logical | TRUE inherits element_blank() from parent slot |
linewidth replaced size in ggplot2 3.4.0. Older tutorials use element_rect(size = 1) for the border. New code should use linewidth; size still works for backward compatibility but throws a deprecation warning.Seven styling patterns with element_rect()
1. Clean white panel background
A white panel.background with light grey90 gridlines is the cleanest baseline and the default for most publication themes. panel.background covers only the data region inside the axes; the area outside the axes is plot.background.
2. Tinted plot background with white panel
plot.background fills the entire image including margins and the title area. Pairing a tinted outer canvas with a white inner panel mimics the look used by FiveThirtyEight and The Economist. Set color = NA on the outer rectangle to avoid a thin border line where the canvas meets the page.
3. Subtle panel border
panel.border is special, it must have fill = NA, otherwise the border overlays the data. Setting fill = NA makes only the border visible. A thin grey80 border at linewidth = 0.5 frames the panel without competing with the data.
4. Colored legend background
Three legend rectangles work together. legend.background is the inner panel behind the legend items. legend.box.background wraps the entire legend area. legend.key is the small rectangle behind each individual key glyph. Match all three when tinting, or the legend looks patchy.
legend.key to match the panel. If panel.background = element_rect(fill = "white"), then legend.key = element_rect(fill = "white", color = NA) blends the key glyphs into the panel. Default key fill is grey and looks out of place against any non-grey panel.5. Bold facet strip background
strip.background paints the header bar above each facet. Dark fill plus white text creates a heading style readers recognize from dashboard layouts. strip.text is the matching text spec; restyle them together or the strip header looks unfinished.
6. Dashed panel border
linetype accepts six string presets or integers 0 through 6. A dashed panel.border reads as a draft or annotation style. The fill must stay NA even when adding a linetype, or the dashes get hidden under the data fill.
7. Invisible but space-preserving
fill = NA and color = NA together draw nothing but keep the layout slot allocated. This differs from element_blank(), which fully drops the element. Use this when the plot is being embedded on a colored web page and the page background should show through.
element_rect(fill = NA) and element_blank() are not interchangeable. Both look empty, but NA reserves space and respects margins; element_blank() removes the slot entirely and can shift panel sizes when aligning multiple plots with patchwork.element_rect() vs the other element_*() helpers
Each rectangle slot accepts only element_rect() or element_blank(). Passing element_text() or element_line() to a rectangle slot throws an error.
| Helper | Used for | Example slot | Returns |
|---|---|---|---|
element_rect() |
Filled rectangles | panel.background, plot.background | Rect spec |
element_line() |
Lines and axes | axis.line, panel.grid | Line spec |
element_text() |
Text styling | plot.title, axis.text | Text spec |
element_blank() |
Hide an element | any slot | Empty spec |
If you assign element_line() to panel.background, R errors with 'panel.background' must be an element_rect object. Match the helper to the slot type.
Common pitfalls
Pitfall 1: Forgetting fill = NA on panel.border. A solid fill on panel.border paints over the data. Always pass fill = NA and set the border via color instead.
Pitfall 2: Using size instead of linewidth. In ggplot2 3.4 and later, element_rect(size = 1) triggers a deprecation warning. Use linewidth = 1 for border thickness.
Pitfall 3: Tinting legend.background only. The legend has three rectangle slots. Restyling one leaves the others grey and patchy. Always set legend.background, legend.box.background, and legend.key together.
Pitfall 4: Confusing panel.background with plot.background. panel.background is the data region inside the axes. plot.background is the entire image including title, axis labels, and margins. Swap them and your tinted color lands in the wrong place.
Try it yourself
Try it: Build a scatter of mpg vs wt from mtcars. Set the panel background to white, the plot background to light grey "#fafafa", add a dashed grey80 panel border, and remove minor gridlines. Save the result to ex_rect.
Click to reveal solution
Explanation: Each rectangle slot gets its own element_rect() call. The panel.border needs fill = NA so the dashed border shows without overlaying the points. The plot.background uses color = NA so the outer canvas does not get a thin frame line.
Related ggplot2 functions
After mastering element_rect(), explore these companions:
theme(): the wrapper that accepts every element_*() specelement_line(),element_text(),element_blank(): matching helpers for lines, text, and hidden elementstheme_minimal(),theme_bw(),theme_classic(): pre-built themes with distinct rectangle styling defaultscoord_cartesian(): pair with panel borders to control where the border meets the datafacet_wrap()andfacet_grid(): produce the strip headers thatstrip.backgroundpaintsguide_legend(): configure legend layout thatlegend.backgroundwraps
For the canonical argument list, see the ggplot2 element reference.
FAQ
How do I change the background color of a ggplot2 plot?
Set plot.background = element_rect(fill = "yourcolor") inside theme() to change the outer canvas, including margins and the title area. To change only the data region inside the axes, set panel.background = element_rect(fill = "yourcolor") instead. Use color = NA on both to suppress the thin border line that ggplot2 adds by default. Built-in themes like theme_minimal() start with a white plot background that pairs well with custom panel tints.
What is the difference between panel.background and plot.background?
panel.background is the rectangle inside the axes where the data is drawn. plot.background is the full image including the title, axis labels, legend, and margins. Setting panel.background = element_rect(fill = "lightblue") tints only the data region; setting plot.background = element_rect(fill = "lightblue") tints the entire image. Use both together to create a panel-with-canvas effect like FiveThirtyEight or The Economist.
How do I add a border around a ggplot2 plot panel?
Set panel.border = element_rect(fill = NA, color = "black", linewidth = 0.5) inside theme(). The fill = NA is mandatory; a solid fill on panel.border paints over the data. Use linewidth to control thickness and linetype for dashed or dotted styles. theme_bw() enables a panel border by default; other themes leave it off.
How do I remove the grey background from a ggplot2 plot?
Either switch to a theme that uses a white panel (theme_minimal(), theme_classic(), theme_bw()), or override the panel directly with theme(panel.background = element_rect(fill = "white")). To remove the panel background entirely so the plot canvas shows through, use panel.background = element_blank(). Pair the change with panel.grid.minor = element_blank() to get a clean Tufte-style look.
Can I use element_rect() to draw rectangles inside the plot?
No. element_rect() only styles theme slots, the panel background, plot background, legend boxes, strip headers, and panel border. To draw rectangles inside the plot panel as data, use geom_rect() or annotate("rect", ...). These geoms accept their own fill, color, and linewidth arguments directly, without element_rect().