
If you have ever built a card grid where titles, excerpts, and "Read more" links stubbornly refuse to line up, you already know the pain that nested grids can cause. You tweak min-height, you duplicate track definitions, you abuse display: contents, and everything still feels fragile.
CSS subgrid is the feature that finally fixes that class of layout problems. It lets nested grids reuse the exact tracks and line names of their parent, so components align as if they were laid out on the same grid — even when they are multiple levels deep.
A recent video on "CSS Subgrid for perfect alignment" shows how subgrid solves those annoying inconsistencies in modern layouts. In this article we will go further: we'll break down how subgrid works, where it shines, where it hurts, and how to integrate it into a modern TypeScript/React codebase and a design system.
Table of Contents
- What CSS Subgrid Actually Is
- How Subgrid Compares to Nested Grids and display: contents
- Subgrid Syntax in Practice
- A Realistic Example: Aligned Card Grid with React + TypeScript
- Subgrid at Page Scale: Macro Grids and Design Systems
- Pros and Cons of Using Subgrid Today
- Browser Support and Progressive Enhancement
- When You Should Reach for Subgrid (and When You Should Not)
- Recommended Resources and Repositories
- Summary
- References
What CSS Subgrid Actually Is
CSS Grid Level 1 gave us two-dimensional layout with rows and columns, but nested grids were always independent. A child grid could not "see" the track sizes or line names of its parent. Aligning nested content meant copying track definitions and hoping nothing drifted out of sync.
Subgrid is a value for grid-template-columns and grid-template-rows:
.subgrid {
display: grid;
grid-template-columns: subgrid;
/* or */
grid-template-rows: subgrid;
/* or both via shorthand */
grid: subgrid / subgrid;
}
Instead of defining its own tracks, a subgrid uses the tracks of the area it spans on its parent grid. That includes:
- Track sizes (
1fr,minmax,auto, etc.). - Gaps between tracks.
- Named grid lines (and you can add more names locally).
In other words, if a subgrid spans three column tracks of the parent, it will have three column tracks of exactly the same size. Children of the subgrid can then align to those tracks as if they were direct children of the parent grid.
How Subgrid Compares to Nested Grids and display: contents
Before subgrid, people tried several imperfect strategies.
Nested grids without subgrid
You define a parent grid, then inside each card (or component) you define another grid:
.cards {
display: grid;
grid-template-columns: repeat(3, minmax(16rem, 1fr));
gap: 1.5rem;
}
.card {
display: grid;
grid-template-rows: auto 1fr auto; /* duplicated */
}
Each nested grid is independent, so header/body/footer alignment across cards is coincidental. Any change to the parent grid must be manually mirrored in the child grids. MDN explicitly calls this out as a core limitation that subgrid solves.
display: contents tricks
A common workaround is to use display: contents on wrappers, so grandchildren become direct grid items and can align to the top-level grid. This has problems:
- It breaks semantics for some use cases (e.g.
<article>,<section>wrappers disappearing from layout but not from the DOM). - It can confuse accessibility tooling and browser devtools.
- It doesn't help when you do want the wrapper to be a layout boundary (e.g. a card with internal grid).
Subgrid instead keeps the wrapper as a real grid item, but allows its internal grid to reuse the parent tracks.
Why experts care
Grid Level 2, and especially subgrid, exist precisely to fix these nested alignment limitations and make lining nested grid items up with the main grid straightforward.
— Rachel AndrewFront-end designer Chen Hui Jing calls Andrew's Grid work "one of the best resources you'll find" if you want to fully understand this layout tool. Subgrid is a central part of that story.
Subgrid Syntax in Practice
At the property level, subgrid is simple:
grid-template-columns: subgrid;grid-template-rows: subgrid;- Or
grid: subgrid / subgrid;for both axes.
A minimal example based on the MDN patterns:
<div class="grid">
<div class="feature">
<div class="feature-inner">
<!-- Nested content here -->
</div>
</div>
</div>
.grid {
display: grid;
grid-template-columns: repeat(9, 1fr);
grid-template-rows: repeat(4, minmax(100px, auto));
}
/* This element is a grid *item* and a grid *container* */
.feature {
display: grid;
grid-column: 2 / 7;
grid-row: 2 / 4;
grid-template-columns: subgrid; /* inherits 5 tracks from parent */
grid-template-rows: subgrid; /* inherits row tracks from parent */
}
.feature-inner {
/* Can place itself using line numbers or names defined on the parent */
grid-column: 3 / 5; /* relative to the subgrid's own line 1..n */
grid-row: 1 / 3;
}
The subgrid adopts the tracks of the area it spans (2 / 7, 2 / 4). Inside .feature, line numbering restarts from 1, but the sizes match the parent.
A Realistic Example: Aligned Card Grid with React + TypeScript
Let's build a simple card layout where titles, bodies, and footers align perfectly across cards, regardless of text length.
The pattern is the same one used in several modern subgrid articles: the container defines three row tracks (auto 1fr auto), and each card uses a row subgrid to reuse those tracks while spanning all three rows. The "1fr" row grows based on the tallest card body.
React + TypeScript component
// components/ArticleGrid.tsx
import React from "react";
import styles from "./ArticleGrid.module.css";
type Article = {
id: number;
title: string;
excerpt: string;
meta: string;
};
const articles: Article[] = [
{
id: 1,
title: "Subgrid for perfectly aligned cards",
excerpt:
"Use CSS subgrid to keep titles, excerpts, and CTAs aligned across cards without duplicating track definitions.",
meta: "5 min read · Layout"
},
{
id: 2,
title: "Design systems and macro grids",
excerpt:
"Define a page-level grid once and let every component automatically snap to the same vertical rhythm.",
meta: "7 min read · Design Systems"
},
{
id: 3,
title: "Progressive enhancement with subgrid",
excerpt:
"Ship clean layouts to modern browsers while keeping older ones usable with a regular nested grid fallback.",
meta: "4 min read · CSS"
}
];
export const ArticleGrid: React.FC = () => {
return (
<section className={styles.section}>
<h1 className={styles.heading}>Latest articles</h1>
<div className={styles.grid}>
{articles.map((article) => (
<article key={article.id} className={styles.card}>
<h2 className={styles.cardTitle}>{article.title}</h2>
<p className={styles.cardExcerpt}>{article.excerpt}</p>
<footer className={styles.cardMeta}>{article.meta}</footer>
</article>
))}
</div>
</section>
);
};
This is standard React + TypeScript: data is typed, the component is self-contained and immediately usable in a Next.js or CRA project.
CSS module using grid-template-rows: subgrid
/* components/ArticleGrid.module.css */
.section {
padding: 2rem clamp(1.5rem, 4vw, 4rem);
background: #020617; /* near-black */
color: #e5e7eb; /* gray-200 */
}
.heading {
font-size: clamp(1.5rem, 3vw, 2rem);
margin-bottom: 1.5rem;
}
/* Parent grid: columns + shared rows for header/body/footer */
.grid {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(18rem, 1fr)
);
/* Three rows: title, flexible body, footer */
grid-template-rows: auto 1fr auto;
gap: 1.5rem;
}
/* Each card becomes a subgrid for rows */
.card {
display: grid;
grid-template-rows: subgrid; /* reuse auto 1fr auto */
grid-row: auto / span 3; /* span the three parent rows */
padding: 1.25rem 1.5rem;
border-radius: 0.75rem;
background: #020617;
border: 1px solid #1f2933;
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.6);
}
/* Children align to the shared row tracks */
.cardTitle {
grid-row: 1;
font-size: 1.125rem;
font-weight: 600;
margin: 0 0 0.5rem;
}
.cardExcerpt {
grid-row: 2;
margin: 0 0 0.75rem;
font-size: 0.95rem;
line-height: 1.5;
}
.cardMeta {
grid-row: 3;
margin-top: auto;
font-size: 0.85rem;
opacity: 0.75;
}
What's happening here:
- The
.gridcontainer defines three row tracks:auto 1fr auto. The middle row is flexible and grows to fit the tallest card body. - Each
.carddeclaresgrid-template-rows: subgridand spans three rows (grid-row: auto / span 3). This tells the card to reuse those three tracks for its own internal layout. - The
h2,p, andfooterassign themselves to rows1,2, and3. Because every card shares the same subgrid rows, titles and footers align perfectly in a row across the entire grid.
This is exactly the kind of layout the video and many blog posts use to show why subgrid feels like a breakthrough compared to pure nested grids.
Subgrid at Page Scale: Macro Grids and Design Systems
One of the most powerful patterns described in the official web.dev article is the idea of a macro grid: a grid covering the whole page (or device viewport), with named rows and columns for things like status bar, nav, header, main, and footer. Nested regions then subscribe to that grid via subgrid, passing track names and sizes down the tree.
A simplified version of that idea:
/* Macro device/page grid */
.device {
display: grid;
grid-template-rows:
[status] 3.5rem
[nav] 3rem
[header] 4rem
[main] auto
[footer] 4rem;
grid-template-columns:
[fullbleed-start] 1rem
[content-start] auto
[content-end] 1rem
[fullbleed-end];
}
/* App container and all its direct children share the same tracks */
.device > .app,
.app > * {
display: grid;
grid: subgrid / subgrid; /* rows and columns from .device */
}
Now any component rendered inside .app can align to the same named lines ([header], [main], [footer], [content-start], etc.) even if it is several levels deep. This is extremely valuable in design systems, where you want:
- Consistent vertical rhythm across independent micro-frontends.
- Predictable placement of full-width sections (hero, banners, promo stripes).
- Shared mental model between designers (Figma grid) and engineers (CSS grid).
Pros and Cons of Using Subgrid Today
Advantages
1. Real alignment for nested layouts
Subgrid aligns nested components to the same tracks as their parents, without duplicating sizing rules. This is ideal for card grids, complex dashboards, and editorial layouts with deep nesting.
2. Cleaner, more maintainable CSS
Instead of repeating grid-template-rows or grid-template-columns in every nested component, you define tracks once and let subgrids reuse them. This reduces drift and makes refactors safer.
3. Better mapping from design to code
Designers often work with a shared grid drawn over an entire page. Subgrid lets you implement that almost literally: a macro grid plus nested subgrids that pass line names downward.
4. Works per axis
You can subgrid only rows, only columns, or both axes. This is useful when, for example, you want consistent vertical alignment but need more freedom horizontally (or vice versa).
Drawbacks and caveats
1. Older browsers still lack support
Modern versions of Chrome (117+), Edge (117+), Firefox (71+), and Safari (16+) support subgrid. But older browsers, legacy mobile browsers, and IE do not.
You should still provide fallbacks, especially for public-facing sites with broad audiences.
2. Debugging becomes more conceptual
When subgrids share tracks and line names, it's easy to forget which grid "owns" which line. Modern DevTools help, but debugging requires a solid understanding of Grid and subgrid semantics.
3. Performance with highly nested layouts
Subgrid itself is not inherently slow, but very deep grid nesting (with or without subgrid) can stress some engines. There have been documented performance issues in complex nested grid layouts on some Safari versions.
4. Fallback logic is subtle
Because subgrid is a valid value even when there is no parent grid, some cascaded fallbacks do not behave as intuitively as authors expect. The CSS Working Group has active discussions around this.
Browser Support and Progressive Enhancement
As of late 2025, the picture is finally good:
- Firefox: full support from version 71 onward.
- Chrome / Edge: support in stable from around 117/118; earlier builds had it behind flags or disabled.
- Safari (macOS & iOS): support since Safari 16.0, including mobile Safari.
MDN marks subgrid as Baseline: newly available since September 2023, meaning it is considered available across current versions of the major engines, but not across all older devices.
CSS-only fallback with @supports
For "new layout, old browser still usable", the classic progressive enhancement pattern is:
/* Baseline: regular nested grid or flexbox */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
gap: 1.5rem;
}
.card {
display: grid;
grid-template-rows: auto 1fr auto; /* duplicated tracks */
}
/* Enhancement: only in browsers that understand subgrid */
@supports (grid-template-rows: subgrid) {
.cards {
grid-template-rows: auto 1fr auto;
}
.card {
grid-template-rows: subgrid;
grid-row: auto / span 3;
}
}
Browsers without subgrid ignore the @supports block and render a normal nested grid; modern browsers upgrade to the subgrid alignment. This pattern matches what many practical guides recommend.
TypeScript helper for feature detection
If you want JS-driven enhancement (e.g. to toggle a specific class or load a subgrid-optimized stylesheet), you can use CSS.supports in a small TypeScript utility:
// utils/subgridSupport.ts
export function supportsSubgrid(): boolean {
if (typeof CSS === "undefined" || typeof CSS.supports !== "function") {
return false;
}
// Test rows or columns; both are fine.
return CSS.supports("grid-template-rows", "subgrid");
}
Use it in a React component:
import React from "react";
import { supportsSubgrid } from "../utils/subgridSupport";
import styles from "./ArticleGrid.module.css";
const hasSubgrid = supportsSubgrid();
export const ArticleGrid: React.FC = () => {
return (
<section
className={
hasSubgrid ? styles.sectionHasSubgrid : styles.sectionNoSubgrid
}
>
{/* ...same JSX as before... */}
</section>
);
};
Then in CSS, you can define separate variants:
.sectionNoSubgrid .card {
display: grid;
grid-template-rows: auto 1fr auto;
}
.sectionHasSubgrid .grid {
grid-template-rows: auto 1fr auto;
}
.sectionHasSubgrid .card {
display: grid;
grid-template-rows: subgrid;
grid-row: auto / span 3;
}
This mirrors what the CSS Working Group and community discussions suggest: use feature detection to gate progressive enhancement, not to emulate subgrid where it doesn't exist.
When You Should Reach for Subgrid (and When You Should Not)
Use subgrid when:
- You have repeating components (cards, tiles, article summaries) that must align in both vertical and horizontal rhythms across a parent grid.
- You maintain a design system with a shared layout grid and want independent components to snap to that grid without leaking layout details into every component.
- You have complex editorial or dashboard pages where nested sections still need to respect a global column or row system.
- You are primarily targeting modern browsers, and older ones only need "good enough" layout rather than pixel-perfect alignment.
Avoid or delay subgrid when:
- Your audience includes a significant portion of legacy browsers (old Android webviews, corporate IE/Edge Legacy, kiosk devices), and alignment is more important than maintainability.
- Layout is simple enough that standard Grid or Flexbox does the job without repetition or hacks.
- You have not yet standardised your grid system. Introducing subgrid without a clear macro grid and naming strategy can make layouts more confusing, not less.
A reasonable migration plan is:
- Define a macro grid and naming convention (
[content-start],[content-end], etc.). - Convert one or two high-value components (e.g. card grid, main layout shell) to subgrid with fallbacks.
- Gather feedback from designers and developers.
- Incrementally roll out across the design system as confidence grows.
Recommended Resources and Repositories
If you want to keep exploring subgrid, these are reliable, up-to-date references:
-
MDN – Subgrid guide (spec details, examples, compatibility)
https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Grid_layout/Subgrid
-
web.dev – CSS subgrid (macro grid patterns, design-oriented examples)
-
Rachel Andrew – Grid Level 2 and Subgrid / book "Get Ready for CSS Grid Layout"
Article and book that dig into Grid Level 2 and subgrid from the spec author's perspective.
-
GitHub – CSS Subgrid Example (card layout)
https://github.com/ditdot-dev/css-subgrid-example – static HTML/CSS example project for card-style layout with subgrid.
-
GitHub – Next.js + CSS Modules subgrid demo
https://github.com/saltycrane/css-subgrid-example – a Next.js project using CSS Modules to demonstrate subgrid in a real React stack.
-
Subgrid overview & baseline info
- Can I Use – feature table: https://caniuse.com/css-subgrid
- Browser compatibility breakdown: https://www.lambdatest.com/web-technologies/css-subgrid
Summary
CSS subgrid is not "just another layout gimmick"; it is the missing link between the way designers think about grids and the way developers implement them. It lets nested components reuse the same track sizes and names as their parents, giving you real alignment without duplicated CSS or brittle hacks.
We now have solid browser support across modern engines. With careful use of @supports, small TypeScript helpers, and a clear grid strategy in your design system, you can start using subgrid today — and retire a whole class of layout workarounds.
References
- MDN Web Docs - Subgrid
- YouTube - Learn CSS Subgrid for Perfect Alignment
- A Book Apart - Get Ready for CSS Grid Layout
- q2bstudio.com - Subgrid CSS: Control de diseño de siguiente nivel
- Stack Overflow - CSS grid-template-rows: subgrid
- DITDOT - CSS Subgrid - How to Build Complex Layouts
- web.dev - CSS subgrid
- FreeCodeCamp - What is CSS Subgrid?
- Can I Use - CSS Subgrid
- GitHub - Nested grid performance in Safari
- GitHub - CSS Working Group Drafts - conditional subgrid fallback
- Apple Developer - Safari 16 Release Notes
- Smashing Magazine - CSS Grid Level 2: Here Comes Subgrid
- GitHub - ditdot-dev/css-subgrid-example
- SaltyCrane Blog - CSS Subgrid demo
- LambdaTest - Browser Compatibility Score of CSS Subgrid