How to Build a CSS Grid Layout (with Real Examples)
A practical guide to CSS Grid: when to use it (and when flexbox is the better choice), the six properties that matter, how named lines and areas work, and how to experiment with a live layout in your browser before committing it to a stylesheet.
CSS Grid is the right layout model for two-dimensional layouts: any case where items need to align across both rows and columns. The six properties that matter are display, grid-template-columns, grid-template-rows, grid-template-areas, gap, and place-items. The most common mistakes are using grid for one-dimensional layouts (use flexbox for that) and over-specifying the layout (let auto-flow do the work). A browser playground lets you edit the values and see the result live, which is the fastest way to learn the model.
CSS Grid is the layout model for two-dimensional layouts. Where flexbox arranges items along a single axis, grid arranges them along two. The two are complementary: flexbox for a row or a column, grid for a real two-dimensional structure. This post covers when to reach for grid (and when to stay with flexbox), the six properties that matter, the named-areas shortcut, and how to experiment with a live layout in your browser before committing it to a stylesheet.
When to use grid (and when not to)
Grid is for two-dimensional layouts: items that need to align across both rows and columns, where the relationship between items matters as much as the items themselves. Concrete cases:
- A page layout with header, sidebar, main content, and footer. The sidebar spans multiple rows; the main content area is the rest. The grid encodes the structure.
- A photo gallery with consistent rows and columns. All rows the same height, all columns the same width, and any cell can be made larger (a featured photo spanning 2 columns, a captioned photo spanning 2 rows).
- A dashboard with widget cards. Each widget occupies a specific cell or group of cells, and the layout is consistent across the dashboard.
- A form with labeled fields. The label column and the input column align across all rows.
Grid is not the right tool for one-dimensional layouts. If you have a row of items that should distribute horizontally, or a column of items that should distribute vertically, flexbox is the simpler choice. The two coexist in most real layouts: the page itself is a grid, and each row of the grid is a flex container.
The six properties that matter
For most layouts, you only need these six properties. Together they describe the entire grid structure.
display: grid
Turns the container into a grid container. Its direct children become grid items. This is the only mandatory declaration to start using grid.
grid-template-columns
Defines the columns. The most common pattern is the responsive auto-fit:
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
This creates as many 250px-or-larger columns as fit, and stretches each to fill its share of the available space. The result is a responsive grid that shows 1 column on phones, 2 on tablets, 3 on small laptops, 4 on desktops, with no media queries. This single line replaces most of the responsive-grid CSS you have ever written.
grid-template-rows
Defines the rows. Less commonly specified than columns; usually auto is enough (rows size to their content). The cases where you want to specify rows: a fixed-height header and footer, a dashboard with rows of specific heights, a masonry-like layout where rows are explicitly sized.
grid-template-areas
Names the cells of the grid so the placement is readable. Example for a classic page layout:
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
Then the items use grid-area: header, grid-area: sidebar, etc. The layout is readable in the CSS itself, without counting lines. The dots (.) represent unnamed cells.
gap
The space between rows and columns. Same as in flexbox. gap: 16px adds 16px between every adjacent row and column. Cleaner than margin on individual items.
place-items
Shorthand for align-items and justify-items at the cell level. The most common value is place-items: center, which centers each item within its cell. The default is stretch, which makes each item fill its cell.
Auto-flow: let the browser do the work
One of the most useful features of grid is grid-auto-flow. The default is row, which means the browser fills the grid one row at a time, placing each new item in the next available cell. For most layouts, this is exactly what you want: you do not need to specify the position of every item.
The alternative is grid-auto-flow: column, which fills the grid one column at a time. This is rarer, but useful for cases where the natural order is column-first (e.g. a list of items in a magazine layout).
The third option is grid-auto-flow: dense, which lets the browser backfill earlier cells with later items if they fit. This is useful for a photo gallery where you want to fill gaps, but can also cause out-of-order rendering that affects accessibility. Use with care.
Spanning rows and columns
Any grid item can span multiple rows or columns. The syntax:
.featured {
grid-column: span 2;
grid-row: span 2;
}
This is the cleanest way to make a "featured" item larger than its neighbors in a photo gallery, or to make a sidebar span the full height of a page. The same effect with named areas:
grid-template-areas:
"header header header"
"sidebar main featured"
"sidebar main featured"
"footer footer footer";
Both syntaxes work; the named-areas version is more readable for non-trivial layouts.
A practical example: a responsive photo gallery
A photo gallery that shows 1, 2, 3, or 4 columns depending on the viewport width, with a featured photo spanning 2 columns. The CSS is six lines:
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
}
.gallery .featured {
grid-column: span 2;
}
That is the entire layout. The browser handles the responsive column count, the gap adds the spacing, and the featured photo spans 2 columns when there is room (and 1 when there is not, because grid is graceful about that).
Another example: a page layout with named areas
A page with a header, sidebar, main content, and footer, where the layout is described in plain English:
.page {
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;
}
.page > header { grid-area: header; }
.page > aside { grid-area: sidebar; }
.page > main { grid-area: main; }
.page > footer { grid-area: footer; }
That is the entire layout. The HTML stays semantic (header, aside, main, footer), the CSS is readable, and the layout is responsive (the sidebar collapses to 100% width on small screens with one media query).
Common gotchas
1. Using grid for one-dimensional layouts
Grid is for two-dimensional layouts. If you have a single row of items, flexbox is the simpler choice. The visual difference is small for a single row, but the CSS is clearer when each layout model is used for its purpose.
2. Forgetting to set the container to display: grid
The most common bug: writing all the grid-template-columns, grid-template-rows, and grid-template-areas on a container, but forgetting display: grid. The result is a normal block container, not a grid, and the items stack vertically as if nothing was specified.
3. Over-specifying the grid
You do not need to specify every cell. With grid-auto-flow: row (the default), the browser places items in the next available cell automatically. Specify only the structural properties (columns, areas, gap); let the browser handle the placement.
4. Using the wrong gap syntax
grid-gap is the old name. The current name is just gap (which works in both flex and grid). The old name still works for backward compatibility, but the new name is the right one to use in new code.
5. Mixing percentage and fr units without thinking about it
1fr means "one share of the available space after the non-fr tracks are sized". If you mix 200px and 1fr, the 1fr track gets whatever is left after the 200px is allocated. If you mix 50% and 1fr, the 1fr track gets whatever is left of the 50% — which can be 0 if the available space is exactly 50%. The fix: use fr units consistently, or use minmax to set a floor.
How to experiment without writing a full HTML file
The CSS Grid Playground on this site is an interactive editor. You write the grid CSS on the left, see the layout on the right, and the result updates live as you type. The playground comes with example layouts (photo gallery, page layout, dashboard) so you can see what each property does, then modify the values to match your use case.
The advantage of the playground over a real stylesheet: you can change one property at a time, see exactly what each one does, and undo if you go down a wrong path. For learning grid or for prototyping a layout, the playground is faster than the edit-reload loop of a real stylesheet.
For the production stylesheet
Once you have the layout right in the playground, the same CSS goes into your stylesheet. The properties are the same, the values are the same, and the result is the same.
For the production site, the CSS Minifier takes the layout CSS (plus the rest of your stylesheet) and produces the minified version that ships to the browser. The minified version is functionally identical, just smaller.
When to use flexbox vs. grid
The mental model that works for most cases:
- Flexbox: one-dimensional layout. A row or a column. Items are independent; the alignment within the row (or column) is the question.
- Grid: two-dimensional layout. Rows AND columns matter. Items have a relationship to each other in both dimensions.
For a real page, the two coexist: the page itself is a grid (header, sidebar, main, footer), and each row of the grid is a flex container (navigation links, form fields, card content). The two layout models are not competing; they are complementary.
A short checklist for new grid layouts
- Is this a two-dimensional layout? If yes, grid. If no, flexbox.
- How many columns?
grid-template-columnswith the auto-fit pattern for most responsive cases. - Are there named regions?
grid-template-areasfor readable structural layouts. - How much space between rows and columns?
gap. - Do any items span multiple rows or columns?
grid-column: span Non the items that need it.
Six properties. That is the entire grid vocabulary for most layouts. The rest is the same kind of layout thinking you would do for any design — but grid makes the two-dimensional structure explicit and easy to maintain.