Web development

Subgrid: three real cases

Subgrid aligns titles, copy and prices across different cards, and lets a component use the page columns. Three cases with code and the real limits.

Key points

  • Subgrid is for a child that must align to the parent tracks, not for one that only needs to sit inside.
  • A child with `grid-template-rows: subgrid` must cover the rows it wants to inherit.
  • On the shared axis gutters come from the parent: redefining `gap` on the child does not move them.

Subgrid solves one precise problem: a component must align to the tracks of its container, not merely sit inside it. Three cases where it earns its place, with the minimum code and the limits no guide mentions.

Case 1: cards with aligned titles and prices

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1.5rem;
}

.card {
  display: grid;
  grid-row: span 4;
  grid-template-rows: subgrid;
}

.card h3,
.card p,
.card .price,
.card .cta {
  margin: 0;
}

The parent declares the rows, the cards inherit them with grid-template-rows: subgrid and the inner margins disappear. Titles, copy and prices start at the same height even when one text runs twice as long.

Case 2: label and value pairs

.specs {
  display: grid;
  grid-template-columns: max-content 1fr;
  grid-template-rows: repeat(4, auto);
  column-gap: 2rem;
  row-gap: 0.5rem;
}

.spec-group {
  display: grid;
  grid-row: span 4;
  grid-template-rows: subgrid;
}

Two columns and four shared rows: labels stay as wide as the longest one and values line up across groups. Flexbox would need a guessed fixed width, which breaks at the first translation.

Case 3: a component inside the page grid

.page {
  display: grid;
  grid-template-columns: 12rem minmax(0, 1fr) 12rem;
  gap: 2rem;
}

.form {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}

.form .field {
  grid-column: 2;
}

The form does not invent a grid of its own: it uses the three page columns, so fields start on the same line as the text above. The component stays reusable because it knows the track count, not the measurements.

When you do not need it

CaseSubgridAlternative
Cards of equal heightNoGrid with `auto` rows and `align-items: stretch`
Titles aligned across cardsYesGuessed min-heights
Component that uses the page columnsYesPadding computed by hand
Grid inside a card, isolatedNoPlain Grid

The record and the command

A finished grid list is grid-list-01; the primitives sit in card and the prices in pricing. The catalogue holds 1,556 blocks from 1,472 libraries (1 November 2026).

# cerca le griglie annidate che potrebbero ereditare le tracce
grep -rn "grid-template-columns" --include="*.css" src/ | wc -l

# verifica il risultato a due larghezze
npx playwright screenshot --viewport-size=1440,900 http://localhost:3000 wide.png

The known limit

The child must cover exactly the rows it wants to inherit: with grid-row: span 3 on a four-row parent, the last row stays outside and the alignment breaks with no warning. The second limit is support: 94.43% leaves out Safari before 16, where the layout falls back to independent grids.

For named areas built on the same idea see Grid with named areas. For the starting layout model see Flexbox or Grid; the comparison between utilities and CSS Modules is Tailwind vs CSS Modules.

Frequently asked questions

Does subgrid work horizontally too?

Yes, with grid-template-columns: subgrid. A component can inherit only columns, only rows, or both.

Must the child be a direct child of the grid?

Yes. Subgrid works between a grid and a direct grid item; a wrapper in between breaks the link and needs display: contents.

How do I handle the gap?

On the shared axis the gutters come from the parent. Redefining gap on the child does not change the alignment, only the space of the unshared tracks.

Does subgrid replace named areas?

No. Areas describe regions of one grid, subgrid links two grids. They work together: the parent holds the areas, the child inherits the tracks.

What happens in browsers without support?

The declaration is ignored and the child becomes an independent grid. Content stays readable, the alignment across cards does not: test it, do not polyfill it.