In the last article, Flexbox handled laying things out in one direction — a row or a column. But sometimes you need both at once: a grid of cards, a page split into a header, sidebar, and content area, an image gallery with neat rows and columns. That’s a two-dimensional problem, and it’s exactly what CSS Grid was built for.
Grid is the most powerful layout tool in CSS. Where Flexbox thinks in a single line, Grid thinks in a full table of rows and columns that you define and then drop things into. It sounds complex, but the basics are surprisingly approachable. Let’s build a few grids.
The core idea: rows and columns
Like Flexbox, Grid works on a container and its items. You turn an element into a grid container with display: grid, and then you define the columns (and optionally rows) you want:
.container {
display: grid;
grid-template-columns: 200px 200px 200px;
}
<div class="container">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
grid-template-columns: 200px 200px 200px says “make three columns, each 200px wide.” The items flow into the grid on their own — the first three fill the first row, and the fourth wraps onto the next. You define the structure; Grid handles the placement.
grid-template-columns defines the column structure
The heart of Grid is grid-template-columns. Each value you list is one column, and that value sets the column’s width — three values, three columns; five values, five columns. Get comfortable with this single property and the rest of Grid starts to click, because everything else builds on it.
The fr unit: flexible columns
Fixed pixel widths are rigid. Grid gives you a special unit to get around that: fr, short for a “fraction” of the available space. It lets columns share the container flexibly:
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
This makes three equal columns that together fill the whole container, whatever its width — each one takes a single fraction of the space. You can mix proportions too:
grid-template-columns: 1fr 2fr; /* second column twice as wide as first */
And mix fixed and flexible:
grid-template-columns: 250px 1fr; /* fixed 250px sidebar, content fills the rest */
That last one is a classic page layout: a fixed-width sidebar with a content area that flexibly takes whatever space is left. The fr unit is what lets Grid layouts adapt smoothly across different screen widths.
repeat() for many columns
When you want a lot of equal columns, typing 1fr 1fr 1fr 1fr gets old fast. The repeat() function does it for you:
.container {
display: grid;
grid-template-columns: repeat(4, 1fr); /* four equal columns */
}
repeat(4, 1fr) means “four columns, each 1fr” — exactly the same as 1fr 1fr 1fr 1fr, just far cleaner. It’s the standard way to lay out an even grid, like a photo gallery or a row of product cards.
Gaps between grid items
Just like Flexbox, Grid has a gap property for spacing between items — and it feels even more at home here, since it controls the gutters between both rows and columns at once:
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
That puts a consistent 20px gap between every row and column. Want different spacing in each direction? Set them separately with row-gap and column-gap. No more fiddling with margins on individual items — gap handles all of it cleanly.
A real card grid
Let’s build something practical: a responsive grid of cards that fits as many columns as the screen allows, all on its own. It uses a slightly more advanced pattern, but one you’ll reach for constantly:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 24px;
}
It looks dense, so let’s unpack it. minmax(250px, 1fr) means “each column is at least 250px, but can grow to fill the space.” auto-fit means “fit as many of those columns as will comfortably go.” Put together, they build a grid that shows four cards on a wide screen, two on a tablet, and one on a phone — all automatically, with no extra code. This one line is among the most useful layout patterns in modern CSS, so keep it handy.
Defining rows too
So far we’ve defined columns and let the rows happen on their own. You can also spell out rows explicitly with grid-template-rows, in exactly the same way:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 100px 200px;
}
This creates a 2×2 grid with a short first row and a taller second one. Defining both rows and columns is what unlocks Grid’s real two-dimensional power — you’re laying out an actual table of cells, not just a line of items.
Grid vs Flexbox: which to use?
You now have two layout tools, so the obvious question is when to use each. Here’s a simple way to decide:
- Flexbox is for one dimension — a single row or a single column. Reach for it for navbars, toolbars, centering, and rows of items that should space out or wrap.
- Grid is for two dimensions — rows and columns together. Reach for it for page layouts, card grids, galleries, and anything that’s genuinely a grid of cells.
You'll use both — often together
It’s not an either/or choice. Real sites run Flexbox and Grid side by side: Grid for the overall page layout, and Flexbox inside individual grid cells to arrange their contents. A classic example is a Grid of cards where each card uses Flexbox to stack its title, text, and button vertically. Learn both, and let the rule of thumb steer you — one dimension, Flexbox; two dimensions, Grid.
Wrapping up
CSS Grid gives you full control over two-dimensional layout:
- Set
display: gridon a container and define columns withgrid-template-columns. - The
frunit shares space flexibly (1fr 1fr= two equal columns;250px 1fr= fixed sidebar + flexible content). repeat(n, 1fr)creates many equal columns cleanly.gapspaces rows and columns consistently.repeat(auto-fit, minmax(250px, 1fr))makes a responsive card grid that adapts to screen width on its own.grid-template-rowsdefines explicit rows for full 2D layouts.- Use Flexbox for one dimension, Grid for two — and often both together.
With Flexbox and Grid in hand, you can build essentially any layout you can picture. The last piece of the puzzle is making those layouts work on every screen size — from a wide monitor down to a phone. That’s the subject of the final CSS article: responsive design, where media queries let your layouts adapt to whatever device a visitor happens to be using.