The Underrated Power of CSS Grid Subgrid
Subgrid finally has solid browser support. Here's how it solves alignment problems that used to require JavaScript hacks, and why you should be using it today.
The Problem Subgrid Solves
CSS Grid is great at laying out direct children. But the moment you nest elements inside those children, the grid lines become invisible to them. Every nested layout starts fresh, and getting things to align across cards or sections required either identical hardcoded heights or JavaScript to measure and synchronize DOM nodes.
This was the card alignment problem. You have a grid of cards. Each card has a title, a description, and a footer with a button. Descriptions vary in length. You want all buttons to line up at the bottom of their respective cards. Before subgrid, your options were bad: fixed heights, flexbox hacks, or JS layout math.
How Subgrid Works
Subgrid lets a child element participate in the parent grid — inheriting its tracks instead of creating new ones:
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr auto; /* title | body | footer */
gap: 1.5rem;
}
.card {
/* Span all 3 row tracks of the parent */
grid-row: span 3;
display: grid;
grid-template-rows: subgrid; /* ← the magic */
}
.card-title { /* automatically in row 1 */ }
.card-body { /* automatically in row 2 — stretches to fill */ }
.card-footer { /* automatically in row 3 — always aligned */ }
The .card element inherits the parent's row tracks. All three children snap to the parent's grid lines. Every card title aligns, every description fills its row, every button lands on the same baseline — without a single line of JavaScript.
Browser Support Today
As of late 2024, subgrid has 93%+ global browser support. Chrome 117, Firefox 71, and Safari 16 all ship it. The only holdouts are very old Android WebViews and some legacy enterprise browsers. For most production apps, you can use it today.
/* Progressive enhancement if you need it */
@supports (grid-template-rows: subgrid) {
.card {
display: grid;
grid-template-rows: subgrid;
}
}
A Real-World Example
Here's the pattern I now use for every card grid:
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-template-rows: auto auto 1fr auto; /* badge | title | desc | footer */
gap: 1rem;
align-items: start;
}
.project-card {
grid-row: span 4;
display: grid;
grid-template-rows: subgrid;
padding: 1.5rem;
border: 1px solid #e2e8f0;
border-radius: 12px;
}
Four lines replace what used to be a ResizeObserver callback and a pile of min-height guesses.
Columns Too
Subgrid works on both axes. If you have a two-column form layout where labels and inputs need to align across rows:
.form { display: grid; grid-template-columns: auto 1fr; gap: .5rem 1rem; }
.form-row { grid-column: span 2; display: grid; grid-template-columns: subgrid; }
Labels in column 1, inputs in column 2, perfectly aligned down the entire form — no flex, no table, no floats.
The Takeaway
Subgrid is one of those features that, once you internalize it, rewires how you think about layout. Stop fighting nested elements. Let them inherit the grid. Your CSS gets shorter, your layouts get more consistent, and you never need JS to align card footers again.
Start using it. The browser support is there.
Related Articles
Optimistic UI Patterns That Actually Work
Real-world patterns for building fast, predictable UIs with Zustand and React Query — and how to handle rollbacks gracefully when things go wrong.