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
/* 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.
.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.
/* 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.
.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
/* 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.
.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."
/* 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:
.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:
.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
.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:
.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-fitwhen you wantauto-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
gapproperty — 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