CSS Subgrid: The Complete Guide for 2026

February 12, 2026

CSS Grid changed how we build layouts. But it had one frustrating limitation: when you nested a grid inside a grid, the inner grid had no relationship to the outer grid's tracks. Every card in a row could end up with a differently-sized header, body, or footer because each card's internal grid was independent. Subgrid solves this by letting a child grid adopt its parent's track definitions, so nested items align perfectly to the outer grid.

This guide covers everything you need to know about CSS subgrid in 2026: the core syntax, the difference between subgrid and nested grids, column and row subgrid, card layouts with consistent alignment, form layouts, named grid lines, gap inheritance, complex page layouts, and practical patterns you can copy into your projects today.

⚙ Build it: Create grid layouts visually with our CSS Grid Generator and explore flexible layouts with the Flexbox Generator.

What Is CSS Subgrid?

Subgrid is a value for grid-template-columns and grid-template-rows. When a grid item is also a grid container, setting one or both of its track definitions to subgrid makes it inherit the parent grid's tracks on that axis instead of defining its own.

.parent {
    display: grid;
    grid-template-columns: 200px 1fr 200px;
    gap: 1.5rem;
}

.child {
    grid-column: 1 / -1;       /* span all parent columns */
    display: grid;
    grid-template-columns: subgrid;  /* use parent's column tracks */
}

The .child now has three columns that are exactly the same as the parent's three columns: 200px, 1fr, and 200px. Any items placed inside .child align to those same tracks. This is the core idea: shared tracks between parent and child.

Why Subgrid Matters

Without subgrid, nested grids are completely independent. Consider a row of three cards, each with a header, body, and footer. If you make each card a grid, each card sizes its rows based on its own content. One card with a long title gets a taller header, and the headers across cards no longer align.

/* WITHOUT subgrid: headers, bodies, footers misalign across cards */
.card-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
}

.card {
    display: grid;
    grid-template-rows: auto 1fr auto;  /* independent rows per card */
}

With subgrid, you define the row structure on the parent, and each card adopts those rows. Every header aligns, every body aligns, every footer aligns, regardless of content length.

Browser Support

As of February 2026, subgrid is supported in all major browsers:

Global support now exceeds 95%. Subgrid is production-ready. If you need to support older browsers, use @supports for progressive enhancement:

/* Fallback for browsers without subgrid */
.card {
    display: grid;
    grid-template-rows: auto 1fr auto;
}

/* Enhancement when subgrid is available */
@supports (grid-template-rows: subgrid) {
    .card {
        grid-template-rows: subgrid;
    }
}

Basic Subgrid Syntax

You can apply subgrid to columns, rows, or both. The child element must be a grid item (placed on the parent grid) and also a grid container (display: grid).

Column Subgrid

.parent {
    display: grid;
    grid-template-columns: 1fr 2fr 1fr;
    gap: 1rem;
}

.child {
    grid-column: 1 / -1;  /* must span the columns you want to inherit */
    display: grid;
    grid-template-columns: subgrid;
}

Row Subgrid

.parent {
    display: grid;
    grid-template-rows: 80px 1fr 60px;
    gap: 1rem;
}

.child {
    grid-row: 1 / -1;  /* must span the rows you want to inherit */
    display: grid;
    grid-template-rows: subgrid;
}

Both Axes

.parent {
    display: grid;
    grid-template-columns: 200px 1fr 200px;
    grid-template-rows: auto 1fr auto;
    gap: 1rem;
}

.child {
    grid-column: 1 / -1;
    grid-row: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    grid-template-rows: subgrid;
}

Mixed: Subgrid on One Axis, Custom on the Other

.child {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;         /* inherit parent columns */
    grid-template-rows: auto 1fr auto;      /* define own rows */
}

This is one of the most useful patterns. You align to the parent's columns but define your own row structure.

Subgrid vs Nested Grid

Understanding the difference is critical. Here is a side-by-side comparison:

/* NESTED GRID: independent tracks, no alignment with parent */
.parent {
    display: grid;
    grid-template-columns: 200px 1fr 200px;
}

.child-nested {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: 200px 1fr 200px;  /* looks the same, but... */
}
/* If parent resizes or changes, child columns do NOT follow.
   They are separate 200px 1fr 200px definitions. */


/* SUBGRID: shared tracks, always aligned */
.child-subgrid {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;  /* linked to parent tracks */
}
/* Child columns are always exactly the parent's columns. */

Key differences:

Card Layouts with Subgrid

The most common use case for subgrid is card layouts where headings, content, and footers need to align across cards. This is the pattern that drove subgrid's design.

/* Parent grid: 3 cards per row, each spanning 3 row tracks */
.card-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, auto);  /* header, body, footer */
    gap: 1.5rem;
}

/* Each card is a subgrid on the row axis */
.card {
    grid-row: span 3;               /* span all 3 row tracks */
    display: grid;
    grid-template-rows: subgrid;    /* adopt parent's row sizing */
    background: var(--surface, #1a1a2e);
    border: 1px solid var(--border, #2a2a3e);
    border-radius: 8px;
    overflow: hidden;
}

.card-header {
    padding: 1.25rem;
    font-weight: 700;
    font-size: 1.1rem;
    border-bottom: 1px solid var(--border, #2a2a3e);
}

.card-body {
    padding: 1.25rem;
    color: var(--text-muted, #9ca3af);
}

.card-footer {
    padding: 1rem 1.25rem;
    border-top: 1px solid var(--border, #2a2a3e);
    display: flex;
    justify-content: flex-end;
    align-self: end;
}

With this setup, all card headers have the same height (determined by the tallest header), all bodies align, and all footers sit at the bottom regardless of content length. No JavaScript, no fixed heights, no hacks.

Form Layouts with Subgrid

Forms with label-input pairs are another natural fit. Without subgrid, aligning labels and inputs across multiple rows requires fixed widths or extra wrappers. Subgrid makes it trivial.

.form-grid {
    display: grid;
    grid-template-columns: max-content 1fr;  /* label | input */
    gap: 1rem 1.5rem;
    align-items: center;
}

/* Each form row spans 2 columns and uses subgrid */
.form-row {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    align-items: center;
}

.form-row label {
    font-weight: 500;
    text-align: right;
    white-space: nowrap;
}

.form-row input,
.form-row select,
.form-row textarea {
    width: 100%;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--border, #333);
    border-radius: 6px;
    background: var(--surface, #1a1a2e);
    color: inherit;
}

Every label column is exactly max-content wide, sized by the longest label. Every input column fills the remaining space. Labels and inputs align perfectly across all rows without any fixed pixel widths.

Form with Validation Messages

.form-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    grid-template-rows: repeat(auto-fill, auto auto);
    gap: 0.25rem 1.5rem;
}

.form-field {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    grid-template-rows: auto auto;  /* input row + error row */
    align-items: center;
}

.form-field label { grid-column: 1; }
.form-field input { grid-column: 2; }

.form-field .error {
    grid-column: 2;     /* error appears under the input, not the label */
    font-size: 0.85rem;
    color: #ef4444;
    min-height: 1.25rem;
}

Named Grid Lines with Subgrid

Named lines from the parent grid pass through to the subgrid. This is powerful for complex layouts where you want semantic placement.

.page {
    display: grid;
    grid-template-columns:
        [full-start] 1fr
        [content-start] minmax(0, 65ch)
        [content-end] 1fr
        [full-end];
    gap: 2rem;
}

.section {
    grid-column: full-start / full-end;
    display: grid;
    grid-template-columns: subgrid;  /* inherits named lines */
}

/* Children of .section can use parent's named lines */
.section h2 {
    grid-column: content-start / content-end;
}

.section .wide-image {
    grid-column: full-start / full-end;  /* breaks out to full width */
}

.section p {
    grid-column: content-start / content-end;
}

You can also add extra named lines on the subgrid itself:

.section {
    grid-column: full-start / full-end;
    display: grid;
    grid-template-columns: subgrid [gutter-start] [main-start] [main-end] [gutter-end];
}

/* Now children can use both parent and subgrid line names */
.section aside {
    grid-column: gutter-start / main-start;
}

Gap Inheritance and Overriding

By default, a subgrid inherits the gap from its parent on the subgridded axis. You can override this if needed.

.parent {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 2rem;
}

/* Inherits parent's 2rem gap */
.child-default {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    /* gap is 2rem, inherited from parent */
}

/* Override with a tighter gap */
.child-tight {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    column-gap: 0.5rem;  /* overrides parent's 2rem */
}

/* Remove gap entirely */
.child-no-gap {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    column-gap: 0;
}

Note that the tracks still align to the parent. Changing the gap does not move the grid lines; it only changes the spacing between them within that subgrid context. The track sizing remains locked to the parent.

Complex Page Layout with Subgrid

Subgrid excels at full-page layouts where sections need to share a column structure. Here is a "content with breakout" pattern commonly used in blogs and documentation sites.

.page-layout {
    display: grid;
    grid-template-columns:
        [full-start] minmax(1rem, 1fr)
        [breakout-start] minmax(0, 8rem)
        [content-start] minmax(0, 65ch)
        [content-end] minmax(0, 8rem)
        [breakout-end] minmax(1rem, 1fr)
        [full-end];
    row-gap: 0;
}

/* Default: content-width items */
.page-layout > * {
    grid-column: content;
}

/* Breakout elements span wider */
.page-layout > .breakout {
    grid-column: breakout;
}

/* Full-bleed elements span edge to edge */
.page-layout > .full-bleed {
    grid-column: full;
}

/* A section that needs internal grid alignment */
.feature-section {
    grid-column: full;
    display: grid;
    grid-template-columns: subgrid;
    padding: 4rem 0;
    background: var(--surface-alt, #111827);
}

.feature-section h2 {
    grid-column: content;
    margin-bottom: 2rem;
}

.feature-section .feature-grid {
    grid-column: breakout;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
}

The .feature-section spans the full width but its content still aligns to the page's content columns. The heading sits within the content column, while the feature grid uses the breakout width. Everything aligns to the same grid structure without hard-coded widths.

Common Patterns and Real-World Examples

Pattern 1: Pricing Table

.pricing-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: auto auto 1fr auto auto;
    /* rows: name, price, features, cta, footnote */
    gap: 1.5rem;
}

.pricing-card {
    grid-row: span 5;
    display: grid;
    grid-template-rows: subgrid;
    border: 1px solid var(--border, #2a2a3e);
    border-radius: 12px;
    overflow: hidden;
    text-align: center;
}

.pricing-card .plan-name { padding: 1.5rem 1rem 0; font-weight: 700; }
.pricing-card .plan-price { padding: 0.5rem 1rem; font-size: 2.5rem; }
.pricing-card .plan-features { padding: 1rem; }
.pricing-card .plan-cta { padding: 1rem; }
.pricing-card .plan-note {
    padding: 0.75rem 1rem;
    font-size: 0.85rem;
    color: var(--text-muted, #9ca3af);
}

Pattern 2: Media Object with Subgrid

.comment-list {
    display: grid;
    grid-template-columns: 48px 1fr;
    gap: 0.75rem 1rem;
}

.comment {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    grid-template-rows: auto auto;
}

.comment .avatar {
    grid-column: 1;
    grid-row: 1 / -1;
    width: 48px;
    height: 48px;
    border-radius: 50%;
}

.comment .author { grid-column: 2; font-weight: 600; }
.comment .body { grid-column: 2; color: var(--text-muted, #9ca3af); }

Best Practices and Gotchas

1. The child must span the parent tracks it wants to inherit. If the parent has 4 columns and the child only spans 2, the subgrid inherits only those 2 column tracks. This is by design, but can be surprising if you forget to set the correct span.

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

/* This child gets 4 subgrid columns */
.full-span {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
}

/* This child gets only 2 subgrid columns */
.partial-span {
    grid-column: 1 / 3;
    display: grid;
    grid-template-columns: subgrid;
}

2. Subgrid items can still be explicitly placed. You can place items inside a subgrid using grid-column and grid-row just like a normal grid. Line numbers are local to the subgrid (starting at 1), not the parent grid.

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

.child {
    grid-column: 2 / 5;  /* spans parent columns 2, 3, 4 */
    display: grid;
    grid-template-columns: subgrid;
}

/* Inside .child, columns are numbered 1-4 (local), not 2-5 */
.child-item {
    grid-column: 1 / 3;  /* maps to parent columns 2-3 */
}

3. Margins and padding on the subgrid container affect layout. Padding on a subgrid container shifts the subgrid tracks inward, which can break alignment with the parent. If you need spacing inside the subgrid container, apply padding to the individual items instead.

/* AVOID: padding on the subgrid container */
.card {
    grid-template-rows: subgrid;
    padding: 1rem;  /* shifts tracks, breaks alignment */
}

/* PREFER: padding on individual items */
.card {
    grid-template-rows: subgrid;
    /* no padding here */
}
.card > * {
    padding: 0 1rem;
}
.card > :first-child { padding-top: 1rem; }
.card > :last-child { padding-bottom: 1rem; }

4. You cannot subgrid from a grandparent. Subgrid only inherits tracks from its direct parent grid. If you need a grandchild to align to a grandparent, the middle element must also be a subgrid.

/* Chaining subgrids through multiple levels */
.grandparent {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
}

.parent {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;  /* inherits from grandparent */
}

.child {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;  /* inherits from parent, which = grandparent */
}

5. Use @supports for progressive enhancement. Provide a reasonable fallback layout (independent grid or flexbox), then enhance with subgrid.

/* Works everywhere */
.card {
    display: grid;
    grid-template-rows: auto 1fr auto;
}

/* Enhanced with subgrid */
@supports (grid-template-rows: subgrid) {
    .card {
        grid-row: span 3;
        grid-template-rows: subgrid;
    }
}

6. Subgrid works with implicit grid tracks. If the parent has implicit tracks created by auto-placement, subgrid can still inherit them. However, explicitly defined tracks are more predictable and easier to reason about.

Frequently Asked Questions

What is CSS subgrid and how is it different from a nested grid?

CSS subgrid allows a grid item that is also a grid container to adopt its parent grid's track sizing instead of defining its own. When you set grid-template-columns: subgrid or grid-template-rows: subgrid, the child's items align to the parent's grid lines. A regular nested grid creates an entirely independent grid with its own track definitions, so child items cannot align to the outer grid. Subgrid solves the alignment problem by sharing tracks between parent and child.

Do all browsers support CSS subgrid in 2026?

Yes. As of 2026, CSS subgrid is supported in all major browsers: Chrome 117+, Edge 117+, Firefox 71+, and Safari 16+. Firefox shipped subgrid first in December 2019. Chrome and Edge added support in September 2023, and Safari added it in September 2022. Global support exceeds 95%, making subgrid safe for production use without fallbacks for most projects.

Can I use subgrid on both columns and rows at the same time?

Yes. You can set both grid-template-columns: subgrid and grid-template-rows: subgrid on the same element to inherit the parent's tracks on both axes. You can also mix subgrid on one axis with custom tracks on the other, for example grid-template-columns: subgrid with grid-template-rows: auto 1fr auto. This flexibility is one of subgrid's most powerful features.

Does subgrid inherit the parent grid's gap?

Yes, by default a subgrid inherits the gap from its parent grid on the subgridded axis. You can override this by setting your own gap, row-gap, or column-gap on the subgrid container. Setting gap: 0 removes spacing even if the parent has gaps. The tracks still align to the parent; only the spacing between them changes within the subgrid context.

When should I use subgrid instead of a regular nested grid?

Use subgrid when child items need to align with the parent grid's tracks. The most common cases are: card layouts where headings, content, and footers must align across cards; form layouts where labels and inputs must align across rows; page layouts where nested sections share the same column structure; and any design where visual alignment across sibling elements matters. Use a regular nested grid when the child needs a completely independent track structure with no relationship to the parent.

Do named grid lines from the parent carry through to a subgrid?

Yes. Named grid lines defined on the parent grid are available inside the subgrid, and children of the subgrid can reference them for placement. You can also add extra named lines on the subgrid by listing them after the subgrid keyword, like grid-template-columns: subgrid [start] [middle] [end]. These names layer on top of any parent names, giving you precise placement control at every nesting level.

Summary

CSS subgrid completes the CSS Grid specification by solving the nested alignment problem that has frustrated developers since Grid launched. Here are the key takeaways:

  1. Use grid-template-columns: subgrid or grid-template-rows: subgrid when child grid items need to align with the parent's tracks.
  2. Card layouts are the primary use case. Subgrid ensures headers, bodies, and footers align across a row of cards without fixed heights or JavaScript.
  3. Form layouts become trivial. Labels and inputs align perfectly when form rows use column subgrid with max-content label tracks.
  4. Named grid lines pass through from parent to subgrid, enabling semantic placement at every nesting level.
  5. Gap is inherited by default but can be overridden on the subgrid container without affecting track alignment.
  6. Avoid padding on the subgrid container as it shifts tracks and breaks alignment. Apply padding to individual items instead.
  7. Browser support is universal as of 2026. Use @supports (grid-template-rows: subgrid) for progressive enhancement if needed.

For more on CSS layout, read our CSS Grid Complete Guide, CSS Flexbox vs Grid Guide, and CSS Container Queries Guide. For hands-on layout building, try our CSS Grid Generator.

Related Resources

CSS Grid Complete Guide
Master CSS Grid for two-dimensional layouts
CSS Flexbox vs Grid
When to use Flexbox vs Grid for your layouts
CSS Container Queries Guide
Component-level responsive design with container queries
CSS Layout Techniques Guide
Complete overview of modern CSS layout methods