The Problem Subgrid Solves

CSS grid gives you a powerful layout engine, but it has a significant limitation: the row and column tracks defined on a parent grid can only be used to position that grid's direct children. If you nest another grid inside, that nested grid starts fresh with its own track definitions. Named grid areas and lines defined on the parent are likewise invisible to anything beyond the direct child.

That all changes with subgrid. Setting grid-template-columns or grid-template-rows to subgrid lets a nested grid inherit the track sizing, templates, and names of its parent's grid. This makes aligning content across nested layouts straightforward.

Before subgrid, creating consistent alignment across variably sized nested content often required hand-tuned layouts that avoided ragged edges like this:

Three cards are shown side by side, each with three bits of content:
header, paragraph and link. Each are of a different text length, creating some
awkward alignments in the cards as they sit next to each other.

After subgrid, that same content can align to shared parent tracks:

Three cards are shown side by side, each with three bits of content:
header, paragraph and link. Each are of a different text length, but subgrid has
fixed the alignments by allowing the tallest of each content item to set the row
height, fixing any alignment issues.

How Subgrid Works

The core idea is simple. Consider a grid container with two named columns. The first track is fixed at 20ch, the second takes the remaining space with 1fr. Named columns aren't required, but they help illustrate the concept:

.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: [column-1] 20ch [column-2] 1fr;
}

Now, take a child of that grid that spans both columns. Make that child a grid container itself, and set its grid-template-columns to subgrid:

.grid > .subgrid {
  grid-column: span 2;

  display: grid;
  grid-template-columns: subgrid; /* 20ch 1fr */
}

A screenshot of the CSS grid DevTools, showing two columns side by side with a name at the start of their column line.
https://codepen.io/web-dot-dev/pen/NWezjXv

That's all it takes. The parent's columns are passed down one level, and the subgrid can place its own children into either of those inherited tracks. The identical principle applies to grid-template-rows.

Building a Macro Grid

Designers commonly work with a shared page-level grid, drawing lines across an entire design to align elements. Before subgrid, that workflow was hard to replicate in CSS because nested components had no access to the parent tracks. Subgrid enables precisely this pattern.

From macro grid to finished design. Grid named areas are created upfront and later components are placed as desired.

A mobile page layout macro grid gives a useful practical example. The DevTools screenshot below shows named rows and columns with clear areas where components should live:

A
screenshot from Chrome CSS grid DevTools showing a mobile sized grid layout
where rows and columns are named for quick identification: fullbleed,
system-status, primary-nav, primary-header, main, footer and system-gestures.

The parent grid defines these named rows and columns with explicit sizes:

.device {
    display: grid;
    grid-template-rows:
      [system-status] 3.5rem
      [primary-nav] 3rem
      [primary-header] 4rem
      [main] auto
      [footer] 4rem
      [system-gestures] 2rem
    ;
    grid-template-columns: [fullbleed-start] 1rem [main-start] auto [main-end] 1rem [fullbleed-end];
}

Styling this parent grid yields a full device layout:

Same CSS DevTools grid overlay as before, but this time with some of the
mobile system UI present, some shadows and a little color. Helps see where the
design is going.

Inside .device sit various nested elements. The outermost column line names are fullbleed-start and fullbleed-end, enabling children to align to both edges simultaneously using the placement shorthand:

A
zoomed in screenshot of the grid overlay from DevTools, focusing specifically on
the fullbleed-start and fullbleed-end column names.

The key step is chaining subgrid down the hierarchy. The app container inherits the rows and columns from .device using subgrid, and each of its children does the same. Every nested grid offers exactly the same named tracks as the top-level device frame:

.device > .app,
.app > * {
    display: grid;
    grid: subgrid / subgrid;

    /* same as */
    grid-template-rows: subgrid;
    grid-template-columns: subgrid;
}

CSS subgrid is a value, not a feature, that replaces a list of grid tracks. When an element spans specific rows or columns on its parent, it makes those same tracks available to its own children. The line names defined on .device thus become accessible to elements deep inside .app, which was impossible before subgrid.

With this setup, a nested image can go full bleed with a declarative one-liner; no negative margins or tricks required:

.app > main img {
    grid-area: fullbleed;
}

The finished macro layout, complete with a full width nested image sitting properly undeath the primary nav and header rows and extending to each of the fullbleed named column lines.
https://codepen.io/web-dot-dev/pen/WNLyjzX

The result is a real macro grid, like the one designers sketch by hand, that scales to components at any depth.

Feature Detection

Subgrid is easy to progressive-enhance. Use @supports to check whether the browser understands the subgrid keyword as a value for the template properties:

@supports (grid-template-columns: subgrid) {
  /* safe to enhance to */
}

DevTools Support

Chrome, Edge, Firefox, and Safari each have grid inspection tools, and the first three include subgrid-specific features. Chrome's tools arrived in version 115; Firefox has had its subgrid support for longer.

Screenshot preview of the subgrid badge found on elements in the Elements
panel.

The subgrid badge works like the standard grid badge but visually distinguishes subgrids from plain grids. When inspecting a layout with multiple nested grids, this badge makes it immediately clear which element is a subgrid and which one defines its own tracks.