Skip to content
CSS

CSS Grid Best Practices

The complete layout guide — when to use Grid, patterns that matter, and mistakes that silently break your layouts.

stellae.design

7 min read

TL;DR

  • CSS Grid is for two-dimensional layouts (rows AND columns); Flexbox is for one-dimensional flow
  • Use grid-template-areas for readable, named layouts that are easy to rearrange in media queries
  • minmax(), auto-fill, and auto-fit create responsive grids without media queries
  • subgrid (now widely supported) lets nested grids align to parent tracks

When to Use Grid

CSS Grid shines when you need to control both rows and columns simultaneously. Page layouts, card grids, dashboards, form layouts — anywhere the structure is two-dimensional. If you're only aligning items in a single row or column, Flexbox is usually simpler.

Defining a Grid

css
/* Explicit grid with fixed and flexible tracks */
.grid {
  display: grid;
  grid-template-columns: 250px 1fr 1fr;
  grid-template-rows: auto 1fr auto;
  gap: 1rem;
}

/* Shorthand with repeat() */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

/* Responsive without media queries */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.5rem;
}

The auto-fill + minmax() pattern is one of the most useful in all of CSS. It creates as many columns as will fit, each at least 280px wide, expanding to fill available space. No media queries needed. Try it live with the stellae.design grid tool at /en/grid.

Named Grid Areas

Grid template areas let you define layouts with readable ASCII art — one of CSS Grid's killer features.

css
.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }

/* Rearrange for mobile */
@media (max-width: 768px) {
  .layout {
    grid-template-columns: 1fr;
    grid-template-areas:
      "header" "main" "sidebar" "footer";
  }
}

auto-fill vs auto-fit

  • auto-fill: Creates empty tracks to fill remaining space. Items stay at their minimum size.
  • auto-fit: Collapses empty tracks. Items stretch to fill all available space.
css
/* auto-fill: cards stay 280px, empty columns fill the rest */
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));

/* auto-fit: if you have 2 cards in a wide container, they stretch */
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));

/* Rule of thumb: auto-fill for card grids, auto-fit when items should stretch */

Subgrid

Subgrid lets a child grid inherit track definitions from its parent, so nested content stays aligned across siblings.

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

.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3; /* card occupies 3 parent rows */
}

/* Now all card titles, bodies, and footers align
   across columns regardless of content length */

Alignment and Placement

css
/* Align the entire grid within its container */
.grid {
  justify-content: center;  /* horizontal */
  align-content: center;    /* vertical */
}

/* Align all items within their cells */
.grid {
  justify-items: center;
  align-items: center;
}

/* Span multiple tracks */
.wide-item { grid-column: 1 / -1; }  /* full width */
.featured { grid-column: span 2; grid-row: span 2; }

Best Practices

  • ✅ Use minmax(0, 1fr) instead of 1fr when content might overflow
  • ✅ Prefer named areas for page-level layouts — they're self-documenting
  • ✅ Use gap instead of margins for grid spacing
  • ✅ Combine Grid for outer layout and Flexbox for inner component alignment
  • ❌ Don't nest grids when subgrid would work
  • ❌ Don't use Grid for simple single-row layouts where Flexbox is simpler
  • ❌ Don't hardcode column counts — use auto-fill/auto-fit for inherent responsiveness

Common Mistakes

  • Content overflow with 1fr: The fr unit respects content size by default (minmax(auto, 1fr)). Long words or images can blow out columns. Use minmax(0, 1fr) and overflow handling.
  • Confusing auto-fill and auto-fit: If your grid looks weird with few items stretching to full width, you probably want auto-fill.
  • Forgetting implicit tracks: Items overflowing your explicit grid go into implicit tracks sized auto. Set grid-auto-rows to control this.
  • Using percentage gaps: gap: 5% can behave unexpectedly. Prefer rem or px for predictable spacing.

Explore Further

Visualize and generate grid layouts with the stellae.design grid tool at /en/grid. For responsive breakpoint testing, check out /en/responsive.

TL;DR

  • CSS Grid is for two-dimensional layouts (rows AND columns simultaneously)
  • Use grid-template-columns: repeat(auto-fill, minmax(min, 1fr)) for responsive grids without media queries
  • Named grid areas make complex layouts readable and maintainable
  • Combine Grid for page layout with Flexbox for component-level alignment

When to Use Grid vs Flexbox

This is the question every developer asks first. The short answer:

  • Grid: When you're controlling rows AND columns — page layouts, card grids, dashboards
  • Flexbox: When you're working in one direction — navbars, button groups, centering In practice, you'll use both. Grid for the macro layout, Flexbox for micro alignment within grid cells.

Grid Fundamentals

Explicit vs Implicit Grids

When you define grid-template-columns, that's your explicit grid. When content overflows into new rows you didn't define, that's the implicit grid.

css
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);  /* explicit: 3 columns */
  grid-auto-rows: minmax(200px, auto);     /* implicit rows: at least 200px */
  gap: 1.5rem;
}

The fr Unit

The fr unit distributes available space proportionally. Think of it as "fraction of remaining space."

css
/* Sidebar + main content */
.layout {
  display: grid;
  grid-template-columns: 280px 1fr;  /* sidebar is fixed, main takes the rest */
  gap: 2rem;
}

/* Three columns where middle is wider */
.layout-wide-center {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;  /* 25% / 50% / 25% */
}

Responsive Grids Without Media Queries

This is Grid's superpower. The auto-fill + minmax pattern creates fully responsive layouts:

css
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 1.5rem;
}

This says: "Fill the row with as many 300px-minimum columns as fit, and stretch them equally to fill remaining space." Resize the browser and cards reflow automatically. No breakpoints needed.

auto-fill vs auto-fit: auto-fill keeps empty tracks; auto-fit collapses them. For most card grids, auto-fill is what you want.

Named Grid Areas

For complex page layouts, named areas are incredibly readable:

css
.page {
  display: grid;
  grid-template-areas:
    "header  header  header"
    "sidebar content aside"
    "footer  footer  footer";
  grid-template-columns: 250px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.aside   { grid-area: aside; }
.footer  { grid-area: footer; }

/* Responsive: stack on mobile */
@media (max-width: 768px) {
  .page {
    grid-template-areas:
      "header"
      "content"
      "sidebar"
      "aside"
      "footer";
    grid-template-columns: 1fr;
  }
}

Alignment and Placement

css
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* Align ALL items */
  justify-items: center;  /* horizontal alignment within cells */
  align-items: center;    /* vertical alignment within cells */
}

/* Align a SINGLE item differently */
.special-item {
  justify-self: end;
  align-self: start;
}

/* Center the entire grid in its container */
.grid-container {
  display: grid;
  place-content: center;  /* shorthand for align-content + justify-content */
  min-height: 100vh;
}

Subgrid

subgrid lets child grids inherit track sizing from their parent. This solves the classic "align card titles across a row" problem:

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

.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;  /* card spans 3 parent rows: image, title, text */
}

Subgrid has excellent browser support as of 2024 — it's safe to use in production.

Best Practices

  • Do use minmax() with auto-fill for responsive grids — it eliminates many media queries
  • Do name your grid areas for complex layouts — it's self-documenting code
  • Do use gap instead of margins for spacing between grid items
  • Don't use Grid for simple single-axis layouts — Flexbox is simpler and sufficient
  • Don't set explicit heights on grid rows unless necessary — let content determine height
  • Don't nest grids more than 2 levels deep — it becomes hard to debug

Common Mistakes

  • Using auto-fit when you want auto-fill — auto-fit stretches a single item to fill the whole row, which usually isn't desired
  • Forgetting min-height: 0 — grid items have min-height: auto by default, which can cause overflow. Set min-height: 0 on items with scrollable content
  • Over-specifying placement — let the auto-placement algorithm do its job. You don't need grid-column and grid-row on every item
  • Ignoring the gap property — using margins on grid items creates uneven spacing at edges

Tools to Explore

  • Grid Generator — visually build CSS grid layouts and export clean code
  • Responsive Tester — preview your grid layouts across device sizes
CSScss

Was this article helpful?