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

facet_wrap() produces one panel per unique value of the faceting variable, arranged in a grid that wraps to the next row when it runs out of horizontal space. The number of rows and columns is chosen automatically to keep panels roughly square, which usually produces a readable layout without manual tweaking:

library(ggplot2)

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

Controlling the grid

The nrow and ncol arguments give you direct control over the panel layout. Set nrow = 4 to stack panels vertically in four rows, or ncol = 2 to force exactly two columns. Specifying both gives you a precise grid that matches your intended page or screen dimensions:

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

Free scales

By default, all panels share the same axis limits. Setting scales = "free" lets each panel scale independently, which magnifies within-panel patterns at the cost of cross-panel comparability. Use "free_x" when x-ranges differ but y-ranges should stay consistent, or "free_y" for the reverse:

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

Multiple faceting variables

Passing multiple variables to vars() creates one panel per unique combination of those variables. The resulting layout is analogous to facet_grid() but without the strict row/column structure — the panels simply wrap into a grid ordered by the variable combinations:

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

Labeller control

The labeller argument customizes how panel strip labels appear. label_both prepends the variable name to each value (producing labels like “cyl: 4, drv: f”), which is helpful when multiple faceting variables would otherwise make it hard to distinguish which value belongs to which variable. Other options include label_value (the default, showing values only) and label_parsed (which treats labels as plotmath expressions):

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

Reordering panels

Panel order follows the underlying factor level order. To change the arrangement, reorder the factor using forcats::fct_reorder() (or base reorder()) before passing it to vars(). The example below sorts class panels by median engine displacement rather than alphabetically, producing a more meaningful visual ordering:

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

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

Strip position

The strip.position argument moves the panel label strips to any side of each panel. Setting it to "bottom" places labels below the plot area, which works well as a subtitle for the x-axis when using free x scales. The "left" position is useful when panel labels are narrow or you have many rows:

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

Repeated data across panels

To show the full dataset as a grey background layer in every panel while highlighting the per-panel subset in the foreground, pass a modified data frame to a background geom layer. Setting the faceting column to NULL in that layer suppresses the faceting for the background, overlaying it uniformly across all panels:

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

Axes and labels

Setting axes = "all" draws axis ticks and labels on every panel rather than only on the outer edges. This is useful when panels span multiple pages or when you want each panel to be fully self-contained for individual inspection:

# 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