CSS Grid is the most powerful layout system available in CSS. Unlike Flexbox, which is one-dimensional, Grid handles both rows and columns simultaneously, making it ideal for complex page layouts. Once you understand Grid's mental model, you'll find yourself reaching for it constantly — and wondering how you ever designed without it.
The Grid Mental Model
A CSS Grid layout consists of:
- A grid container — the parent with
display: grid - Grid items — direct children of the container
- Grid tracks — the rows and columns that define the grid structure
- Grid lines — the dividing lines between tracks (numbered from 1)
- Grid cells — the intersection of a row and column
- Grid areas — named rectangular regions spanning one or more cells
Defining a Grid
grid-template-columns and grid-template-rows
These properties define the track sizes. Values can be lengths, percentages, fr units (fractions of available space), or the auto keyword.
.container {
display: grid;
/* Three equal columns */
grid-template-columns: 1fr 1fr 1fr;
/* Shorthand with repeat() */
grid-template-columns: repeat(3, 1fr);
/* Mixed sizes: fixed sidebar, flexible content, fixed sidebar */
grid-template-columns: 200px 1fr 200px;
/* Two rows: auto height, then 100px */
grid-template-rows: auto 100px;
/* Minimum 120px, grows to fit content */
grid-template-rows: minmax(120px, auto);
}
The fr unit is the key to Grid. It represents a fraction of the available space after fixed-size tracks are accounted for. 1fr 2fr 1fr creates three columns where the middle takes twice the space of the outer ones.
gap
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px; /* same for rows and columns */
row-gap: 16px; /* or set separately */
column-gap: 24px;
}
repeat() and minmax()
/* repeat(count, size) */
grid-template-columns: repeat(4, 1fr); /* 4 equal columns */
grid-template-columns: repeat(4, 200px); /* 4 fixed-width columns */
/* minmax(min, max) — useful for responsive grids */
grid-template-columns: repeat(3, minmax(200px, 1fr));
/* auto-fill vs auto-fit with minmax — responsive without media queries */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
auto-fill creates as many tracks as will fit, even if some are empty. auto-fit collapses empty tracks, letting filled tracks expand. For most responsive grids, auto-fit is what you want.
Placing Items on the Grid
grid-column and grid-row
Items are placed using line numbers. Grid lines are numbered starting from 1, and negative numbers count from the end.
.item {
/* span from line 1 to line 3 (2 columns wide) */
grid-column: 1 / 3;
/* span from line 2 to line 4 */
grid-column: 2 / 4;
/* use span keyword */
grid-column: 1 / span 2; /* start at 1, span 2 columns */
grid-column: span 3; /* auto-placed, but 3 columns wide */
/* last column using negative index */
grid-column: 1 / -1; /* full width */
/* rows */
grid-row: 1 / 3; /* span 2 rows tall */
}
grid-area
grid-area is shorthand for row-start / column-start / row-end / column-end:
.item {
/* grid-area: row-start / col-start / row-end / col-end */
grid-area: 1 / 1 / 3 / 4;
}
Named Grid Areas
Named areas are the most readable way to define complex layouts. Use grid-template-areas on the container and grid-area on items.
.layout {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"sidebar content aside"
"footer footer footer";
min-height: 100vh;
gap: 16px;
}
header { grid-area: header; }
.sidebar { grid-area: sidebar; }
main { grid-area: content; }
aside { grid-area: aside; }
footer { grid-area: footer; }
Each string in grid-template-areas represents a row. Cells with the same name form a rectangular area. Use a period (.) for empty cells.
.layout {
grid-template-areas:
"header header header"
"sidebar content ." /* empty cell in last column */
"footer footer footer";
}
Alignment
Container-level alignment
justify-items and align-items control how items are aligned within their grid cells.
.container {
/* Horizontal alignment of items within cells */
justify-items: start | end | center | stretch; /* default: stretch */
/* Vertical alignment of items within cells */
align-items: start | end | center | stretch; /* default: stretch */
/* Shorthand */
place-items: center; /* center both axes */
place-items: start end; /* align-items start, justify-items end */
}
justify-content and align-content control how the entire grid is distributed within the container (when the grid is smaller than the container).
.container {
justify-content: start | end | center | space-between | space-around | space-evenly;
align-content: start | end | center | space-between | space-around | space-evenly;
place-content: center; /* shorthand */
}
Item-level alignment
.item {
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
place-self: center; /* shorthand */
}
Auto-Placement
Items that aren't explicitly placed are auto-placed into the grid. grid-auto-flow controls how auto-placement works.
.container {
grid-auto-flow: row; /* default: fill rows first */
grid-auto-flow: column; /* fill columns first */
grid-auto-flow: row dense; /* fill holes with smaller items */
grid-auto-flow: column dense;
}
/* grid-auto-rows / grid-auto-columns: set implicit track sizes */
.container {
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(100px, auto); /* any implicit rows default to this */
}
The dense keyword is great for masonry-like layouts — it packs items into any available gaps.
Real-World Layout Patterns
Responsive Card Grid (No Media Queries)
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 24px;
padding: 24px;
}
This single rule creates a responsive grid that shows as many columns as fit, each at least 280px wide. No media queries needed.
Full-Page App Layout
.app {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr;
grid-template-areas:
"sidebar topbar"
"sidebar content";
height: 100vh;
}
.sidebar { grid-area: sidebar; overflow-y: auto; }
.topbar { grid-area: topbar; }
.content { grid-area: content; overflow-y: auto; }
/* Responsive: collapse sidebar on mobile */
@media (max-width: 768px) {
.app {
grid-template-columns: 1fr;
grid-template-rows: 64px auto 1fr;
grid-template-areas:
"topbar"
"sidebar"
"content";
}
}
Magazine Layout
.magazine {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 16px;
}
.featured-article {
grid-column: 1 / 4; /* spans 3 columns */
grid-row: 1 / 3; /* spans 2 rows */
}
.secondary-article {
grid-column: 4 / 7; /* spans last 3 columns */
}
.sidebar-widget {
grid-column: 4 / 7;
grid-row: 2;
}
Image Gallery with Large Feature Image
.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 200px;
gap: 8px;
}
.gallery-item:first-child {
grid-column: 1 / 3; /* featured: 2 columns wide */
grid-row: 1 / 3; /* and 2 rows tall */
}
Centering a Single Item
.page {
display: grid;
place-items: center;
min-height: 100vh;
}
Grid vs Flexbox: When to Use Which
- Use Grid when you need a two-dimensional layout — rows AND columns must align. Page layouts, data tables, image galleries.
- Use Flexbox when you're distributing items along a single axis — a row of buttons, a nav bar, a vertical list.
- They're designed to work together: Grid for macro layout, Flexbox for micro-components within grid cells.
Subgrid
Subgrid (CSS Grid Level 2, now supported in all modern browsers) lets a nested grid inherit tracks from its parent, enabling precise alignment across components.
.parent {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.child {
display: grid;
grid-column: span 2;
grid-template-columns: subgrid; /* inherit parent columns */
}
Quick Reference
/* Container */
display: grid | inline-grid
grid-template-columns: <track-sizes>
grid-template-rows: <track-sizes>
grid-template-areas: "..." "..."
gap: <row> <col>
grid-auto-flow: row | column | dense
grid-auto-rows: <size>
grid-auto-columns: <size>
justify-items: start | end | center | stretch
align-items: start | end | center | stretch
place-items: <align> <justify>
justify-content: start | end | center | space-*
align-content: start | end | center | space-*
/* Items */
grid-column: <start> / <end>
grid-row: <start> / <end>
grid-area: <name> | <row-start> / <col-start> / <row-end> / <col-end>
justify-self: start | end | center | stretch
align-self: start | end | center | stretch
place-self: <align> <justify>
/* Useful functions */
repeat(count, size)
minmax(min, max)
fit-content(size)
auto-fill / auto-fit
CSS Grid is worth spending a few hours experimenting with. Create a simple page layout, try named grid areas, then build a responsive card grid without any media queries. Once it clicks, you'll never go back to float-based layouts or complex Flexbox hacks for two-dimensional problems.
Free Developer Tools
If you found this article helpful, check out DevToolkit — 40+ free browser-based developer tools with no signup required.
Popular tools: JSON Formatter · Regex Tester · JWT Decoder · Base64 Encoder
🛒 Get the DevToolkit Starter Kit on Gumroad — source code, deployment guide, and customization templates.
Top comments (0)