Aligning nested grids with subgrid
By default a nested CSS grid defines its own tracks, so its rows and columns do
not line up with the outer grid. The subgrid value makes a nested grid adopt
the parent’s grid lines on a chosen axis, so deeply nested content aligns to one
shared track structure. This reference covers the syntax and inheritance rules.
The classic problem subgrid solves is form-layout alignment: a card grid where each card holds a label, an input, and a help line. Without subgrid every card sizes its own rows independently, so the inputs in adjacent cards land at different heights. With subgrid all cards share the parent rows, making them align perfectly without JavaScript measurement.
How it works
Set display: grid on the child, then set grid-template-columns: subgrid
and/or grid-template-rows: subgrid. The child must itself be a grid item; the
parent lines it inherits are exactly the ones its placement spans:
.parent {
display: grid;
grid-template-columns: [a] 1fr [b] 2fr [c] 1fr [d];
gap: 1rem;
}
.child {
grid-column: a / d; /* spans all three parent columns */
display: grid;
grid-template-columns: subgrid; /* adopts a / b / c / d */
}
The child’s columns now align pixel-for-pixel with the parent’s, inheriting the
named lines a–d and the 1rem gap. You can append your own names —
subgrid [start] [mid] — and override column-gap/row-gap if you accept the
resulting misalignment.
Span, line names, and gap inheritance in detail
Three pieces of behaviour trip people up:
Span controls the inherited tracks. If .child spans two of the four parent
columns, it inherits only two tracks. Widening the span to three columns adds a
third inherited track. You cannot inherit more tracks than you span.
Named lines accumulate. Parent line names are passed into the subgrid context
intact. You can add extra names with bracket notation after subgrid:
grid-template-columns: subgrid [label-end] [input-start];
These extra names exist only inside the subgrid; the parent never sees them.
Gap behaviour. By default the subgrid adopts the parent’s column-gap or
row-gap on the subgridded axis. Setting a different value on the subgrid element
shifts its tracks relative to the parent, which breaks column alignment. Override
only when you deliberately want a misaligned sub-layout.
Mixing subgrid and independent axes
You can apply subgrid to one axis while the other remains independent:
.card {
grid-column: span 1;
grid-row: 1 / 4; /* spans three parent row tracks */
display: grid;
grid-template-columns: 80px 1fr; /* independent columns */
grid-template-rows: subgrid; /* inherits parent rows */
}
Here the card’s columns are still autonomous, but its rows lock to the parent structure, so labels and content heights align across neighbouring cards even though each card controls its own column widths.
Browser support and fallback
Subgrid shipped in Firefox 71 (2019), Safari 16 (2022) and Chromium 117 (2023).
Global support covers all evergreen browsers. For older browsers the property
degrades silently to an independent grid — the layout still works, but items in
sibling cards no longer align. A safe progressive-enhancement pattern is to let
the fallback use auto row sizing and add the subgrid in a @supports block:
@supports (grid-template-rows: subgrid) {
.card { grid-template-rows: subgrid; }
}
Tips and notes
- Subgrid only mirrors the tracks the item spans — placement controls extent.
- Inherited line names let you write
grid-column: a / binside the subgrid. - Both axes can be subgrid; mix subgrid on one axis with normal tracks on the other.
- Provide a non-subgrid fallback for legacy browsers, as it degrades silently.
- Dev tools in Chrome, Firefox and Safari all visualise subgrid tracks separately from the parent, making it much easier to debug which tracks are inherited.