CSS Grid
Two-dimensional layout with display: grid — template columns and rows, gap, grid-area, and a dashboard example.
Grid vs flexbox
Flexbox is one-dimensional — it lays items out along a single axis (a row, or a column). CSS Grid (display: grid) is two-dimensional — it lets you define rows and columns simultaneously and place items anywhere within that structure. Grid is the right tool whenever a layout needs to align content both horizontally and vertically at once, like a full page layout or a dashboard.
.container {
display: grid;
}
Defining the grid
.grid {
display: grid;
grid-template-columns: 200px 1fr 1fr; /* 3 columns: fixed, flexible, flexible */
grid-template-rows: 80px auto 60px; /* 3 rows: header, content, footer */
gap: 1rem;
}
grid-template-columns/grid-template-rows— define the size of each column/row, space-separated.fr("fraction") — a unit unique to grid: divides remaining space proportionally.1fr 2frsplits leftover space into thirds, giving the second column twice the first's share.gap— spacing between grid cells (same idea as flexbox'sgap).
The repeat() function avoids repetitive declarations:
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr); /* 4 equal columns */
gap: 1rem;
}
Auto-fit / auto-fill responsive grids
A very common pattern — a grid that automatically adds or removes columns based on available width, with zero media queries:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
}
Each column is at least 220px, and as many columns as will fit are created automatically — resize the viewport and the number of columns adjusts on its own.
Placing items: grid-area and line-based placement
By default, items fill the grid left-to-right, top-to-bottom, one per cell. You can explicitly place an item using grid line numbers or named areas.
.item {
grid-column: 1 / 3; /* start at column line 1, end at column line 3 (spans 2 columns) */
grid-row: 2 / 4; /* start at row line 2, end at row line 4 */
}
Named template areas are often more readable for page-level layouts:
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"sidebar header"
"sidebar main"
"sidebar footer";
min-height: 100vh;
}
.sidebar { grid-area: sidebar; }
.header { grid-area: header; }
.main { grid-area: main; }
.footer { grid-area: footer; }
<div class="layout">
<aside class="sidebar">Sidebar</aside>
<header class="header">Header</header>
<main class="main">Main content</main>
<footer class="footer">Footer</footer>
</div>
The ASCII-art-like grid-template-areas value is one of grid's most readable features — the CSS literally looks like a diagram of the layout.
Practical example: a dashboard layout
<div class="dashboard">
<header class="dashboard-header">Dashboard</header>
<nav class="dashboard-nav">Nav</nav>
<main class="dashboard-main">Main content</main>
<aside class="dashboard-aside">Stats</aside>
<footer class="dashboard-footer">Footer</footer>
</div>
.dashboard {
display: grid;
grid-template-columns: 220px 1fr 280px;
grid-template-rows: 64px 1fr 48px;
grid-template-areas:
"nav header header"
"nav main aside"
"nav footer footer";
gap: 1rem;
min-height: 100vh;
}
.dashboard-header { grid-area: header; }
.dashboard-nav { grid-area: nav; }
.dashboard-main { grid-area: main; }
.dashboard-aside { grid-area: aside; }
.dashboard-footer { grid-area: footer; }
/* collapse to a single column on small screens */
@media (max-width: 768px) {
.dashboard {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"nav"
"main"
"aside"
"footer";
}
}
This is the classic "holy grail" layout (header, footer, nav, main, sidebar) — something that historically required floats and negative margins, and now takes about a dozen lines of grid CSS, including the responsive collapse.
Grid vs flexbox — when to use which
| Flexbox | Grid | |
|---|---|---|
| Dimensions | One (row or column) | Two (rows and columns) |
| Best for | Navbars, button groups, aligning items along one line | Full page layouts, dashboards, image galleries |
| Sizing driven by | Content | The explicit grid definition |
| Item placement | Follows source order (mostly) | Can be placed anywhere in the grid explicitly |
In practice, most real UIs use both: grid for the overall page structure, flexbox for aligning content within an individual grid cell (like centering a button inside a card).
Common mistakes
- Reaching for grid to align a single row of items (like a navbar) — flexbox is simpler and more idiomatic for one-dimensional alignment.
- Forgetting that grid line numbers start at 1, not 0, and that
grid-column: 1 / 3means "from line 1 to line 3" (spanning 2 tracks), not "columns 1 through 3." - Not testing
auto-fit/auto-fillgrids at very narrow widths — without a sensibleminmax()lower bound, columns can become uncomfortably narrow before wrapping. - Mixing up
grid-template-areasnames with typos — a single mismatched name between the template and an item'sgrid-areasilently drops that item into the default flow instead of erroring.