CSS Grid: The Layout System You Should Actually Be Using
A practical guide to CSS Grid covering grid-template-columns, fr units, grid areas, auto-fit/auto-fill, and building responsive layouts without media queries.
Flexbox gets all the attention, but CSS Grid is what you actually want for page-level layouts. Flexbox is great for one-dimensional stuff -- a row of buttons, a navbar, centering something. Grid handles two-dimensional layouts: rows AND columns at the same time. Most real interfaces need both, and knowing when to reach for which one makes your CSS dramatically simpler.
The Basics
Turn any element into a grid container:
.container {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
gap: 16px;
}
That gives you a three-column layout where the sidebars are 200px and the center takes up whatever space is left. The gap property adds spacing between grid items (no more margin hacks).
The fr Unit
fr stands for "fraction of available space." It's the unit you'll use most with Grid.
grid-template-columns: 1fr 2fr 1fr;
This divides the available width into 4 parts: 1 for the first column, 2 for the middle, 1 for the last. The browser handles the math. You don't need to think in percentages or calc().
You can mix fr with fixed units:
grid-template-columns: 250px 1fr;
Sidebar is always 250px. Main content gets everything else. This is the holy grail layout in two lines of CSS.
Placing Items on the Grid
By default, items flow into the grid left-to-right, top-to-bottom. But you can place them explicitly:
.header { grid-column: 1 / -1; } / Span all columns /
.sidebar { grid-column: 1; grid-row: 2; }
.main { grid-column: 2; grid-row: 2; }
.footer { grid-column: 1 / -1; }
The 1 / -1 syntax means "from the first grid line to the last." Grid lines are numbered starting at 1 (not 0 -- CSS isn't JavaScript).
Named Grid Areas (the Good Stuff)
This is where Grid gets really expressive. Instead of juggling line numbers, you can name areas and lay them out visually:
.container {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 16px;
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
Read that grid-template-areas block. It literally looks like the layout. That's the whole point -- you can see the structure at a glance.
Want to change the layout for mobile? Just redefine the areas:
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
}
Same HTML, completely different layout. Two declarations changed.
Responsive Grids Without Media Queries
Here's the trick that makes Grid incredible for card layouts:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
Break that down:
repeat()-- repeat a column patternauto-fill-- create as many columns as will fitminmax(280px, 1fr)-- each column is at least 280px, but grows to fill space
auto-fill vs auto-fit
These are almost identical, with one difference:
- auto-fill creates empty tracks if there's extra space
- auto-fit collapses empty tracks so items stretch to fill
auto-fillgives you 3 items and 2 empty column slotsauto-fitgives you 3 items that stretch wider to fill the row
auto-fill. Use auto-fit when you have very few items and want them to stretch.
Grid vs Flexbox: When to Use Which
Use Grid when:- You need to control both rows and columns
- You're building a page-level layout
- You want items to align in a strict grid (like a card layout or dashboard)
- You're doing anything that looks like a table but isn't tabular data
- You're arranging items in a single row or column
- You need items to wrap naturally without strict column alignment
- You're building component-level layouts (a button with an icon, a nav bar)
- You need items to grow/shrink based on content
Real Example: Dashboard Layout
.dashboard {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr;
grid-template-areas:
"nav header"
"nav content";
height: 100vh;
}
.nav { grid-area: nav; }
.header { grid-area: header; }
.content { grid-area: content; overflow: auto; }
/ Cards inside the content area /
.content-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
padding: 20px;
}
That's a full dashboard shell -- fixed sidebar, header bar, scrollable content area with a responsive card grid inside. About 20 lines of CSS.
Gotchas
Grid items stretch by default. Child elements fill their grid cell both horizontally and vertically. Usealign-self and justify-self to control this, or place-items on the container.
Percentage heights can be weird. If your grid container doesn't have an explicit height, percentage-based row sizing might not work as expected. Use fr or auto instead.
Subgrid support is still limited. grid-template-columns: subgrid lets nested grids align to the parent grid. It's in Firefox and Safari but Chrome only added it recently. Check caniuse before relying on it.
Practice It
Grid is one of those things that clicks way faster when you're actually writing and tweaking code than when you're just reading about it. If you want to experiment with Grid layouts in real time, CodeUp has an interactive environment where you can see your changes rendered instantly -- no local setup needed.
The syntax feels verbose at first, but Grid replaces entire frameworks worth of layout code. Once you internalize grid-template-columns, fr, and grid-template-areas, you'll look at your old float-based and flexbox-hacked layouts and wonder how you ever put up with them.