CSS Grid is the most powerful layout system ever added to CSS. With it, you can create complex two-dimensional layouts that were previously impossible without JavaScript or deeply nested markup. This guide will take you from the basics to advanced techniques.
What is CSS Grid?
CSS Grid is a two-dimensional layout system — it handles both rows and columns simultaneously. Compare this to Flexbox, which is primarily one-dimensional (either a row or a column).
Use Grid for page layouts and UI regions. Use Flexbox for component-level alignment and one-directional flows.
The Grid Container
Everything starts with display: grid:
.container {
display: grid;
/* Define 3 columns: fixed | flexible | auto */
grid-template-columns: 200px 1fr auto;
/* Define 2 rows */
grid-template-rows: 60px 1fr;
/* Gap between cells */
gap: 1rem; /* row-gap + column-gap */
column-gap: 2rem;
row-gap: 1rem;
}
The fr Unit
The fr (fraction) unit represents a fraction of the available space after fixed sizes are allocated:
/* Three equal columns */
grid-template-columns: 1fr 1fr 1fr;
/* Sidebar + main + sidebar */
grid-template-columns: 250px 1fr 250px;
/* Ratio of 1:2:1 */
grid-template-columns: 1fr 2fr 1fr;
Defining Grid Areas
Named grid areas make layouts readable and maintainable:
.layout {
display: grid;
grid-template-areas:
"header header header"
"sidebar main main "
"footer footer footer";
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
header {
grid-area: header;
}
aside {
grid-area: sidebar;
}
main {
grid-area: main;
}
footer {
grid-area: footer;
}
This creates a classic blog layout:
┌───────────────────────────────┐
│ HEADER │
├──────────┬────────────────────┤
│ SIDEBAR │ MAIN │
│ │ │
│ │ │
├──────────┴────────────────────┤
│ FOOTER │
└───────────────────────────────┘
Placing Items on the Grid
You can place items explicitly using line numbers:
.item {
/* grid-column: start-line / end-line */
grid-column: 1 / 3; /* Span from line 1 to line 3 */
grid-column: 1 / -1; /* Span entire row */
grid-column: span 2; /* Span 2 columns from current position */
/* grid-row: start-line / end-line */
grid-row: 2 / 4;
/* Shorthand: row-start / column-start / row-end / column-end */
grid-area: 2 / 1 / 4 / 3;
}
Repeat and Auto Functions
repeat()
Avoid repeating yourself when defining tracks:
/* ❌ Repetitive */
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
/* ✅ Concise */
grid-template-columns: repeat(12, 1fr);
/* Mixed: 2 fixed columns then 3 flexible */
grid-template-columns: 200px 100px repeat(3, 1fr);
minmax()
Define a range for track sizes:
/* Column that's at least 200px but can grow */
grid-template-columns: minmax(200px, 1fr);
/* Row that's at least 100px but sizes to content */
grid-template-rows: minmax(100px, auto);
auto-fill and auto-fit
Create responsive grids without media queries:
/* auto-fill: Fill as many columns as fit, keeping empty tracks */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
/* auto-fit: Same but collapses empty tracks */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
This creates a responsive card grid that goes from 1 column on mobile to as many as fit on desktop:
Mobile (320px): | Card |
Tablet (768px): | Card | Card | Card |
Desktop (1440px): | Card | Card | Card | Card | Card | Card |
Grid Alignment
Control how items are aligned inside their grid cells:
.container {
/* Align all items along the row axis (horizontal) */
justify-items: start | end | center | stretch;
/* Align all items along the column axis (vertical) */
align-items: start | end | center | stretch;
/* Shorthand */
place-items: center; /* align-items + justify-items */
}
.item {
/* Override alignment for specific items */
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
place-self: center;
}
Aligning the Grid Itself
When the grid is smaller than the container:
.container {
/* Distribute columns within the container */
justify-content: start | end | center | stretch | space-around | space-between
| space-evenly;
/* Distribute rows within the container */
align-content: start | end | center | stretch | space-around | space-between |
space-evenly;
}
Real-World Patterns
Holy Grail Layout
.holy-grail {
display: grid;
grid-template:
"header" auto
"nav main aside" 1fr
"footer" auto
/ 200px 1fr 200px;
min-height: 100vh;
}
Responsive Card Grid
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
gap: 1.5rem;
}
The min(100%, 300px) prevents cards from being narrower than their container on very small screens.
Full-Bleed Layout
For a blog where most content is centered but some items (like images) should break out:
.content {
display: grid;
grid-template-columns:
[full-start] minmax(0, 1fr)
[wide-start] minmax(0, 100px)
[content-start] min(65ch, 100%)
[content-end] minmax(0, 100px)
[wide-end] minmax(0, 1fr)
[full-end];
}
.content > * {
grid-column: content;
}
.content > .wide {
grid-column: wide;
}
.content > .full-bleed {
grid-column: full;
}
Masonry Layout (CSS-native)
CSS masonry is in the specification but not widely supported yet. As a polyfill approach:
/* Two-column masonry with Flexbox */
.masonry {
display: flex;
flex-direction: column;
flex-wrap: wrap;
max-height: 1000px; /* Must set a height */
}
.masonry > * {
width: calc(50% - 1rem);
}
Subgrid
Subgrid (now well-supported) lets nested grids participate in the parent grid:
.parent {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto;
gap: 1rem;
}
/* Child spans all 3 columns and uses parent's column tracks */
.child {
grid-column: span 3;
display: grid;
grid-template-columns: subgrid; /* Uses parent's 3 columns! */
}
This is incredibly useful for aligning items across cards:
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid; /* Title, description, and button all align! */
}
Debugging CSS Grid
Use browser DevTools to visualize your grid:
- Chrome/Edge: Open DevTools → Elements panel → click the
gridbadge - Firefox: Superior grid inspector with named line visualization
- CSS: Add
outline: 2px solid redto grid children temporarily
/* Quick debug helper */
.debug * {
outline: 1px solid rgba(255, 0, 0, 0.3);
}
When to Use Grid vs Flexbox
| Situation | Use Grid | Use Flexbox |
|---|---|---|
| Overall page layout | ✅ | |
| Two-dimensional layouts | ✅ | |
| Content-driven sizing | ✅ | |
| Navigation bar | ✅ | |
| Card content layout | ✅ | |
| Centering a single item | Either | ✅ |
| Vertical + horizontal control | ✅ |
Browser Support
CSS Grid has excellent browser support (97%+ globally). Subgrid is now supported in all modern browsers as of 2023.
/* Progressive enhancement for older browsers */
.layout {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
}
Conclusion
CSS Grid has fundamentally changed how we build web layouts. The key insights to take away:
- Grid is for 2D layouts, Flexbox is for 1D
- Named areas make your CSS self-documenting
frunits +minmax()unlock responsive layouts without media queriesauto-fit/auto-fillwithminmax()is the go-to responsive card pattern- Subgrid solves the alignment problems we’ve been fighting for years
Experiment in the browser, use the DevTools grid inspector, and refer to CSS-Tricks Grid Guide as a reference. Grid clicks quickly once you start using it in real projects.