plain nested grid → subgrid: inheriting the parent's tracks
A normal nested grid creates its own column tracks — they only
line up with the parent by coincidence (same fractions, same width).
grid-template-columns: subgrid makes the child
inherit the parent's tracks directly, so
columns always align — even when the parent uses unequal tracks
(1fr 2fr 1fr), nested cards, or form label/input pairs.
| concept | plain nested grid | subgrid |
|---|---|---|
| track source | child defines its own (grid-cols-3 → 3 equal new tracks) |
child inherits the parent's exact tracks — no new ones created |
| alignment with parent | coincidental — breaks if parent tracks are unequal or have gaps | guaranteed — same track sizes, same gaps, same lines |
| when parent tracks change | child does NOT follow — drifts out of alignment | child follows automatically — always re-aligns |
| number of child cells | any number (child's own track count) | must span the parent tracks it consumes (col-span-3 → 3 sub-tracks) |
| Tailwind v4 syntax | grid grid-cols-3 |
grid grid-cols-subgrid (+ col-span-N) |
| browser support | all browsers (grid since 2017) | Chrome 117+, Safari 16+, Firefox 71+ (Baseline 2023) |
1 · see the alignment — subgrid vs plain grid
The parent grid uses unequal tracks 1fr 2fr 1fr
(narrow, wide, narrow). Row A is the parent's own header row — the gold
standard. Row B is a col-span-3 child using
subgrid — its three cells line up perfectly
with Row A. Row C is a col-span-3 child using a
plain grid-cols-3 — it makes its OWN equal tracks and
visibly drifts off the parent's wide middle column. Watch the measured
widths below each row.
row A · track 1
parent cell
row A · track 2 (wide)
parent cell
row A · track 3
parent cell
row B · subgrid 1
inherits track 1
row B · subgrid 2
inherits track 2 (wide)
row B · subgrid 3
inherits track 3
row C · plain 1
own track (equal)
row C · plain 2
own track (too narrow)
row C · plain 3
own track (equal)
subgrid grid-template-columns: —
parent [—, —, —] · subgrid [—, —, —] · plain [—, —, —]
The width readout proves the alignment numerically: subgrid cell widths always equal the parent's row-A cell widths; the plain row's middle cell is narrower than the parent's wide track (that's the drift).
2 · when to reach for subgrid
| use case | why subgrid | pattern |
|---|---|---|
| nested cards aligned to parent columns | a card's header/body/footer must span the same column widths as siblings | col-span-3 grid grid-cols-subgrid |
| form label / input pairs across columns | labels and inputs stay in lock-step with the page's column rhythm | form wrapper as subgrid child, fields drop into inherited tracks |
| row headers that must match data columns | header row and body row share identical track geometry | both rows are subgrid children of one parent grid |
| responsive columns that change at breakpoints | parent switches from 1fr 1fr to 1fr 2fr 1fr; children follow with zero restyling |
only restyle the parent — subgrid children auto-align |
| deeply nested layouts (card inside card inside grid) | each level inherits the level above — alignment propagates through the whole tree | chain of grid-cols-subgrid down the nesting |
| avoid: simple equal-column layouts | if parent and child both want 3 equal columns, plain grid-cols-3 already aligns — subgrid adds nothing |
use plain grid-cols-3 instead |
intent → pattern
| intent | pattern | why |
|---|---|---|
| child inherits parent column tracks | <div class="col-span-3 grid grid-cols-subgrid"> |
grid-template-columns: subgrid — no new tracks created |
| child inherits parent ROW tracks | <div class="row-span-3 grid grid-rows-subgrid"> |
grid-template-rows: subgrid — same idea, vertical |
| match gaps too | grid grid-cols-subgrid gap-2 |
gap is taken from the parent track definition; keep it consistent |
| consume a subset of parent tracks | col-span-2 grid grid-cols-subgrid (2 cells inside) |
child spans 2 parent columns → subgrid exposes exactly those 2 tracks |
| use parent's named lines | parent grid-cols-[1fr 2fr [mid] 1fr] → child col-start-mid |
subgrid inherits line names from the parent — address them directly |
| progressive enhancement fallback | @supports (grid-template-columns: subgrid) |
ship plain grid for old browsers, subgrid as enhancement |