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
| Parameter | Description |
|---|---|
facets | Use vars(var1, var2) to specify one or more faceting variables. Also accepts a formula (~var) or a character vector (c("var1", "var2")) for compatibility. |
nrow | Number of rows in the grid. |
ncol | Number 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
| Parameter | Description |
|---|---|
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
| Parameter | Description |
|---|---|
shrink | TRUE (default), scales shrink to fit the statistics output. FALSE, scales use the raw data range before statistical transformation. |
drop | TRUE (default), unused factor levels are dropped automatically. FALSE — all factor levels are shown regardless of whether they appear in the data. |
strip.position | Where to place facet labels: "top" (default), "bottom", "left", "right". |
dir | Layout 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
| Parameter | Description |
|---|---|
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
| Feature | facet_wrap | facet_grid |
|---|---|---|
| Variables | Any number, wrapped | Exactly row + col variables |
| Grid shape | Automatic 2D layout | Explicit row × column |
| Best for | Single variable with many levels | Two variables in a true grid |
| Scales | Per-panel or shared | Per-panel or shared |
| Empty cells | Possible (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
- /tutorials/r-data-visualization/faceting-in-ggplot2/ — faceting tutorial with both facet_wrap and facet_grid
- /tutorials/r-data-visualization/ggplot2-basics/ — getting started with ggplot2
- /tutorials/r-data-visualization/ggplot2-facets-and-themes/ — themes and styling facets