subgrid layout

[check: …]
📖 Pair this live Tailwind demo with the companion guide (.md) — this page is the rendered ground truth. ↗ Builds on frontend/foundations: CSS grid (parent grid tracks, line names — subgrid is the mechanism that lets a child inherit them instead of redefining its own).

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.

conceptplain nested gridsubgrid
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 casewhy subgridpattern
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

intentpatternwhy
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