Articles

CSS Subgrid Explained: Nested Grids That Align

CSS subgrid lets a nested grid item inherit its parent's row and column tracks, so child elements line up across unrelated components.

Takina Takina · · 3 min read
Close-up of CSS code on a screen

Subgrid is a CSS Grid feature that lets a nested grid item inherit the row or column tracks of its parent grid instead of defining its own, so content inside separate child elements can align to the exact same lines. It solves a specific, long-standing layout problem: cards, list rows, or components that each manage their own internal grid but need their internal pieces — a title, a price, a button — to line up with the same items in sibling components.

The problem subgrid solves

Ordinary CSS Grid lets you define rows and columns for one container, but each nested grid inside a grid item is independent — it has no knowledge of the parent’s track sizes. Picture a row of pricing cards, each with a title, description, and price stacked vertically. If one card’s description is longer than another’s, its price gets pushed down, and the row of prices no longer lines up across cards, even though every card individually uses a grid.

Before subgrid, fixing this required either JavaScript to measure and equalize heights, or restructuring the whole layout into a single flat grid — which breaks the natural one-component-per-card structure and makes the markup harder to reason about.

How it works

A normal nested grid declares its own tracks:

.parent {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto auto auto;
}

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

By setting grid-template-rows: subgrid on .card, the card doesn’t invent its own row sizing — it adopts the row tracks of its parent grid for exactly the number of rows it spans (grid-row: span 3 here). Every child inside .card that’s placed into row 1, 2, or 3 now aligns to the same line as the corresponding row in every sibling card, because they’re all measuring against the same set of tracks.

Subgrid works the same way for columns with grid-template-columns: subgrid, and a nested grid can use subgrid on one axis while defining its own independent tracks on the other.

A concrete example

.card-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.card {
  grid-column: span 1;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 4;
  gap: 0.5rem;
}
<div class="card-row">
  <div class="card">
    <h3>Starter</h3>
    <p>Short description.</p>
    <p class="price">$9/mo</p>
    <button>Choose</button>
  </div>
  <div class="card">
    <h3>Pro</h3>
    <p>A much longer description that wraps across two lines.</p>
    <p class="price">$29/mo</p>
    <button>Choose</button>
  </div>
</div>

Even though the “Pro” card’s description wraps to two lines, its .price and <button> still land on the same grid lines as the “Starter” card’s, because both cards inherit the same row tracks from .card-row rather than sizing their own rows independently.

Subgrid vs a single flat grid

You could get similar alignment by making every title, description, and price a direct child of one giant grid instead of nesting cards. That works for simple cases, but it means giving up component boundaries — the markup structure has to match the visual grid exactly, which gets unwieldy once cards have conditional content, different numbers of children, or need to be generated by a loop in a framework. Subgrid keeps each card as a self-contained component while still letting it participate in the parent’s alignment.

Flat single gridSubgrid
Component boundariesMust match grid structurePreserved
Alignment across siblingsAutomatic (same grid)Automatic (inherited tracks)
Works with variable child countsAwkwardStraightforward
Nesting depthOne levelAny depth, each level opts in

Where it fits with other layout tools

Subgrid is specifically about track inheritance across a nesting boundary, which makes it complementary to rather than a replacement for container queries, which handle responsive sizing based on a container’s own dimensions, and the CSS cascade, which governs which styles apply in the first place. A component can use a container query to decide how many columns to show and subgrid to align its internal content to a parent’s tracks at the same time — the two features operate on different axes of the layout problem.

The takeaway

Subgrid lets a nested grid item stop inventing its own row or column tracks and instead line up with its parent’s, which is exactly what’s needed when independent components — cards, table-like rows, list items — need their internal pieces to align across siblings without flattening the markup into one grid. Reach for it whenever equal-height rows or column alignment across repeated components would otherwise require JavaScript measurement.

Takina Takina · · 4 min read

CSS object-fit and object-position, Explained

object-fit controls how an image or video is cropped inside its box, and object-position controls which part of it stays visible. How they work together.

#CSS #Web Development #Frontend
Takina Takina · · 5 min read

CSS inherit, initial, unset & revert Explained

CSS's four global keywords control where a property's value comes from. How inherit, initial, unset, and revert differ, with a comparison table.

#CSS #Web Development #Frontend
Takina Takina · · 4 min read

Dynamic Viewport Units: dvh, svh, and lvh Explained

dvh, svh, and lvh fix the classic mobile vh bug where browser toolbars cut off full-height layouts. Here's what each unit measures and when to use it.

#CSS #Web Development #Frontend