rguides

ggplot2::facet_wrap

facet_wrap arranges a sequence of panels into a 2D grid. It takes a 1D set of facets — produced by one or more categorical variables — and wraps them into rows and columns. This is the right choice when you have a single faceting variable with many levels, or multiple variables but not in a full grid.

Signature

facet_wrap(
  facets,
  nrow = NULL,
  ncol = NULL,
  scales = "fixed",
  space = "fixed",
  shrink = TRUE,
  labeller = "label_value",
  as.table = TRUE,
  switch = deprecated(),
  drop = TRUE,
  dir = "h",
  strip.position = "top",
  axes = "margins",
  axis.labels = "all"
)

Returns: A FacetWrap object (a ggproto object controlling panel layout).

Parameters

Facet Specification

ParameterDescription
facetsUse vars(var1, var2) to specify one or more faceting variables. Also accepts a formula (~var) or a character vector (c("var1", "var2")) for compatibility.
nrowNumber of rows in the grid.
ncolNumber of columns in the grid.

You specify either nrow or ncol, or both. If you specify neither, ggplot2 picks a layout that keeps panels roughly square and fills the space efficiently.

Scale Behavior

ParameterDescription
scales"fixed" (default) — all panels share the same scale. "free" — each panel gets its own scale based on its data. "free_x" — x scale free, y scale shared. "free_y" — y scale free, x scale shared.
space"fixed" (default) — all panels have the same size. "free_x" — panel widths proportional to x range, constrained to one row. "free_y" — panel heights proportional to y range, constrained to one column.

Panel Appearance

ParameterDescription
shrinkTRUE (default) — scales shrink to fit the statistics output. FALSE — scales use the raw data range before statistical transformation.
dropTRUE (default) — unused factor levels are dropped automatically. FALSE — all factor levels are shown regardless of whether they appear in the data.
strip.positionWhere to place facet labels: "top" (default), "bottom", "left", "right".
dirLayout direction: "h" (horizontal, default) fills row-by-row; "v" fills column-by-column. You can also use two-letter combos like "tr" (top-right start, fill downward).

Axis Display

ParameterDescription
axes"margins" (default) — axes drawn at exterior margins only. "all" — draw axes at all panels. "all_x" — x axes at all panels. "all_y" — y axes at all panels.
axis.labels"all" (default) — labels shown on all interior axes. "margins" — labels only on exterior axes. "all_x" / "all_y" — labels only on interior axes in that direction.

Basic Example

library(ggplot2)

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(class))

This produces one panel per class value, arranged in a grid. The number of rows and columns is chosen automatically to keep panels roughly square.

Controlling the Grid

# 4 rows, auto number of columns
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(class), nrow = 4)

# 2 columns, auto number of rows
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(class), ncol = 2)

Specifying both nrow and ncol gives you precise control over the layout.

Free Scales

# Each panel scales independently
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(class), scales = "free")

Free scales make it easier to spot within-panel patterns, but harder to compare values across panels. Use "free_x" when x ranges differ but y ranges should be comparable, or "free_y" for the opposite.

Multiple Faceting Variables

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(cyl, drv))

When you supply multiple variables to vars(), facet_wrap creates one panel per combination — like a grid without the row/column structure of facet_grid. The grid layout follows the same wrapping rules.

Labeller Control

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(cyl, drv), labeller = label_both)

label_both shows both variable names and values (e.g., “cyl: 4, drv: f”). Other options include label_value (values only, default), label_parsed (treats labels as plotmath expressions), and custom functions.

Reordering Panels

The order of panels follows the order of factor levels. To change the order:

mpg$class_ordered <- reorder(mpg$class, mpg$displ)

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(class_ordered))

reorder() sorts factor levels by a second variable — here, median displacement per class.

Strip Position

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(class), strip.position = "bottom")

Moving strips to the bottom works well as a subtitle for the x-axis, especially with free x scales. "left" is useful when you have narrow labels or many panels.

Repeated Data Across Panels

ggplot(mpg, aes(displ, hwy)) +
  geom_point(data = transform(mpg, class = NULL), colour = "grey85") +
  geom_point() +
  facet_wrap(vars(class))

By setting class = NULL in a separate layer’s data, you can display the full dataset as a background in every panel while the foreground data is filtered per panel.

Axes and Labels

# Show axes at every panel
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(class), axes = "all")

# Show y-axis labels at interior panels too
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap(vars(class), axis.labels = "all_y")

When scales are fixed, you can choose which panels get axes and which get axis labels. "all" for axes draws the axis lines everywhere; "all_x" / "all_y" targets specific directions.

facet_wrap vs facet_grid

Featurefacet_wrapfacet_grid
VariablesAny number, wrappedExactly row + col variables
Grid shapeAutomatic 2D layoutExplicit row × column
Best forSingle variable with many levelsTwo variables in a true grid
ScalesPer-panel or sharedPer-panel or shared
Empty cellsPossible (irregular grid)Never (full rectangular grid)

Use facet_wrap when you have one variable (or two without needing a true grid). Use facet_grid when you want a fixed row × column layout.

See Also