CSS Subgrid — the layout primitive we waited six years for.
Subgrid shipped in Firefox in 2019. Then nothing for four years. Then Safari 16 in 2022, Chrome 117 in 2023. As of 2025, every browser supports it. The wait was long, the implementation is excellent, and the things it makes possible are genuinely new — not faster versions of old patterns. Here are the five layouts that were not possible before subgrid and the gotchas nobody warns you about.
01The problem Subgrid solves
Before subgrid, every nested grid was its own track system. If you had a parent grid defining three columns, and put a card inside that card needed its own internal columns aligned with the parent's, you had two choices: hardcode the dimensions (fragile), or use Flexbox inside (different alignment behavior). Neither was right.
Subgrid fixes this by letting a child grid inherit its parent's track definitions. The child says "use the parent's columns" or "use the parent's rows" — and now every grandchild aligns to the same line as everything else in the layout, no matter how deep the nesting goes.
The keyword is one line: grid-template-columns: subgrid or grid-template-rows: subgrid. That's it. Five years of CSS Working Group debate condensed into eight characters.
02Recipe 1 — Card content alignment
The most common subgrid use case. A grid of cards where each card has a title, body, and meta line. Without subgrid, the title lines and meta lines in different cards drift apart based on content length. With subgrid, every card's row boundaries align to the same lines.
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr auto; /* title, body, meta */
gap: 16px;
}
.cards .card {
display: grid;
grid-template-rows: subgrid; /* inherits parent's rows */
grid-row: span 3; /* spans all 3 parent rows */
gap: 8px;
}
The mountain
A short body. Even though this card has less text, the meta line below still aligns with the others.
An unusually long card title that wraps across two or three lines depending on width
A medium-length body that fills the space between the title and the meta. The bottom edge stays consistent.
Short
A much longer body. Without subgrid, this card's meta line would drift downward and break the visual rhythm. With subgrid, every card's three regions land on the same lines: title row, body row, meta row.
auto 1fr auto). Each child says "use those rows" via grid-template-rows: subgrid, and grid-row: span 3 claims all three of them. The child doesn't need to know what rows the parent has — only how many to claim.03Recipe 2 — Form labels that actually align
Building a form where labels live above inputs and the input columns align across rows is something everyone has built ten times with different hacks. Subgrid makes it trivial.
.form {
display: grid;
grid-template-columns: 200px 1fr;
gap: 20px 12px;
}
.form .field-group {
display: grid;
grid-column: 1 / -1; /* span both parent cols */
grid-template-columns: subgrid; /* inherit them */
}
.form label { grid-column: 1; }
.form input { grid-column: 2; }
Now you can group related fields inside <fieldset>-like containers, give them their own borders or backgrounds, and the label/input columns still align to the master grid. Before subgrid, this required either flat structure (no semantic grouping) or duplicated track definitions on every group.
04Recipe 3 — Nested navigation that respects parent gaps
A horizontal nav bar with primary and secondary levels. The secondary level needs to align to the same item it's nested under. With subgrid, the secondary row inherits the column structure of the primary.
.nav {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: auto auto;
gap: 8px 24px;
}
.nav .secondary {
grid-column: 1 / -1;
grid-row: 2;
display: grid;
grid-template-columns: subgrid;
gap: 24px;
}
05Recipe 4 — Article layout with optional sidebar
Articles often have content + an optional sidebar (table of contents, footnotes, author bio). Subgrid lets you define a content grid at the page level, and have individual blocks (pull quotes, figures, asides) escape into the sidebar column without losing their alignment.
.article {
display: grid;
grid-template-columns: 1fr min(65ch, 100%) 1fr;
column-gap: 32px;
}
.article > * { grid-column: 2; } /* default: content column */
.article .figure {
display: grid;
grid-template-columns: subgrid;
grid-column: 1 / -1; /* span all 3 page columns */
}
.article .figure img { grid-column: 1 / -1; } /* full bleed */
.article .figure figcaption { grid-column: 2; } /* back to text col */
06Recipe 5 — Pricing table where every feature row aligns
The classic pricing table: three plan columns, each with a feature list. The feature names in plan A must align horizontally with feature names in plans B and C. Without subgrid, you either flatten the structure (lose per-plan styling) or hardcode row heights. With subgrid, each plan is its own grid that inherits the parent's row definitions.
.pricing {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto repeat(5, 1fr) auto;
gap: 0 16px;
}
.pricing .plan {
display: grid;
grid-template-rows: subgrid;
grid-row: 1 / -1;
background: var(--bg-2);
border-radius: 12px;
padding: 20px;
}
Now the plan name row, price row, five feature rows, and CTA row all align across all three plans, regardless of how long any individual feature description is.
07Gotchas nobody warns you about
Subgrid is one-directional. You inherit rows OR columns, not both at once with a single keyword. If you want both, you write grid-template-rows: subgrid; grid-template-columns: subgrid; as two separate declarations. They're independent decisions.
The child must span the inherited tracks. A subgrid is meaningless if it claims only one row of the parent. You almost always pair grid-template-rows: subgrid with grid-row: span N (or explicit start/end lines). Forgetting this is the most common bug.
Gaps don't always inherit. The parent's gap applies between subgrid tracks. But if the subgrid declares its own gap, that overrides for spacing within the subgrid. Read carefully — this is easy to get wrong.
You can still place items normally. Inside a subgrid, you don't have to use every track. grid-column: 2 places an item in the second inherited column. grid-column: 1 / -1 spans all of them. The placement syntax is identical to regular grid.
DevTools shows subgrid tracks differently. Chrome and Firefox both have subgrid-aware grid inspection. The subgrid lines appear as dashed (instead of solid) to indicate they're inherited. If you've never noticed this, look closer next time you inspect.
∞What changes when you internalize this
Before subgrid, the layouts above required one of: hardcoded dimensions, JavaScript measurement, table layout hacks, or accepting that things wouldn't quite align. Every front-end developer has shipped at least one of those compromises and felt bad about it.
Subgrid removes the compromise. The layout is honest now: you say "these things should align," and they do, with semantic markup, with arbitrary content lengths, with no measurement code. That's a real shift. The CSS Working Group took six years to deliver it. They got it right.