CSS Grid
The Two-Dimensional Layout Tool
CSS Grid handles layout in both directions at once -- rows and columns. Where flexbox excels at distributing items along a single axis, grid excels at placing items on a defined two-dimensional plane.
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
}
That creates a three-column grid where each column takes an equal fraction of the available space. Items are placed automatically, filling each cell left to right, top to bottom.
Defining Columns & Rows
grid-template-columns
/* Three equal columns */
.grid { grid-template-columns: 1fr 1fr 1fr; }
/* Shorthand with repeat */
.grid { grid-template-columns: repeat(3, 1fr); }
/* Mixed units */
.grid { grid-template-columns: 250px 1fr 1fr; }
/* Fixed sidebar, flexible main, fixed aside */
.grid { grid-template-columns: 250px 1fr 300px; }
grid-template-rows
/* Explicit row heights */
.grid {
grid-template-rows: 80px 1fr 60px;
}
/* Rows auto-size to content by default */
/* You only need grid-template-rows when you want explicit control */
The fr Unit
fr stands for fraction. It distributes remaining space after fixed-size columns are accounted for.
.grid {
grid-template-columns: 200px 1fr 2fr;
/* 200px is fixed
remaining space split: 1 part + 2 parts
if container is 800px: 200px + 200px + 400px */
}
fr is like flex-grow for grid tracks. It is the unit you will use most often.
Gap
.grid {
display: grid;
gap: 16px; /* Equal row and column gap */
/* Or separately: */
row-gap: 24px;
column-gap: 16px;
}
gap replaces the older grid-gap property. It works the same as in flexbox -- space between items, not around the edges.
Placing Items
By default, grid items fill cells automatically in order. For precise control, you place items on specific grid lines.
Grid Lines
Grid lines are numbered starting at 1:
Column lines: 1 2 3 4
│ │ │ │
Row lines: 1 ─┼────┼────┼────┼
2 ─┼────┼────┼────┼
3 ─┼────┼────┼────┼
Placing an Item
.header {
grid-column: 1 / 4; /* Span from line 1 to line 4 (all 3 columns) */
grid-row: 1 / 2; /* First row */
}
.sidebar {
grid-column: 1 / 2; /* First column */
grid-row: 2 / 3; /* Second row */
}
.main {
grid-column: 2 / 4; /* Second and third columns */
grid-row: 2 / 3; /* Second row */
}
Span Syntax
Instead of specifying start and end lines:
.header {
grid-column: span 3; /* Span 3 columns from wherever placed */
}
.wide-card {
grid-column: span 2; /* Take up 2 columns */
}
Grid Template Areas
Named areas make complex layouts readable:
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: 80px 1fr 60px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}
.page-header { grid-area: header; }
.page-sidebar { grid-area: sidebar; }
.page-main { grid-area: main; }
.page-footer { grid-area: footer; }
<div class="page">
<header class="page-header">Header</header>
<aside class="page-sidebar">Sidebar</aside>
<main class="page-main">Main Content</main>
<footer class="page-footer">Footer</footer>
</div>
The visual shape of the grid-template-areas value mirrors the actual layout. This is one of the most readable layout techniques in CSS.
Use a dot (.) for empty cells:
grid-template-areas:
"header header header"
"sidebar main ."
"footer footer footer";
Auto-Fit & Auto-Fill with Minmax
This is where grid truly shines for responsive design -- responsive grids without media queries.
minmax()
Sets a minimum and maximum size for a track:
.grid {
grid-template-columns: repeat(3, minmax(200px, 1fr));
/* Each column: at least 200px, at most 1fr */
}
auto-fit
Creates as many columns as fit, collapsing empty tracks:
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 16px;
}
This creates a responsive card grid:
- On a 1200px screen: 4 columns of ~280px each
- On an 800px screen: 3 columns of ~250px each
- On a 600px screen: 2 columns of ~280px each
- On a 300px screen: 1 column at full width
No media queries. The grid adapts automatically.
auto-fill
Similar to auto-fit, but keeps empty tracks:
/* auto-fill: empty tracks maintain their space */
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
/* auto-fit: empty tracks collapse, items stretch to fill */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
The difference matters when you have fewer items than columns. auto-fit stretches items to fill the row. auto-fill preserves the column width and leaves empty space.
Use auto-fit in most cases. It gives you the fluid behavior users expect.
Implicit Grid
When items are placed outside the explicit grid (more items than defined cells), the grid creates implicit tracks automatically. Control their size with grid-auto-rows and grid-auto-columns:
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(100px, auto); /* At least 100px, grow with content */
}
Alignment
Grid uses the same alignment properties as flexbox, in two dimensions. justify-content and justify-items work horizontally. align-content and align-items work vertically. Individual items override with justify-self and align-self.
.grid {
display: grid;
grid-template-columns: repeat(3, 200px);
justify-content: center; /* Center the grid in its container */
align-items: center; /* Center items vertically within cells */
}
.special-item {
justify-self: end; /* Override for one item */
}
Common Patterns
Responsive Card Grid
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 24px;
}
The simplest and most useful grid pattern. Cards fill available space, wrap naturally, and no media queries are needed.
Subgrid
subgrid lets a nested grid inherit track sizes from its parent grid. This solves alignment issues in nested layouts.
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.card {
display: grid;
grid-template-rows: subgrid; /* Inherit row tracks from parent */
grid-row: span 3; /* Card spans 3 rows */
}
With subgrid, card titles, bodies, and footers align across cards even when their content lengths differ. Browser support for subgrid is now available in all modern browsers.
Common Pitfalls
- Using grid for one-dimensional layouts: A single row of items is flexbox territory. Grid adds complexity without benefit for single-axis layouts.
- Forgetting
minmaxwithauto-fit/auto-fill:repeat(auto-fit, 250px)creates fixed-width columns that may overflow. Useminmax(250px, 1fr)for fluid behavior. - Confusing
auto-fitandauto-fill: With fewer items than columns,auto-fillleaves empty space whileauto-fitstretches items. Test with small item counts. - Not setting
grid-auto-rows: Without it, implicit rows size to content, which can create uneven row heights. Usegrid-auto-rows: minmax(100px, auto)for consistency. - Over-specifying placement: Let auto-placement do the work when items follow a natural order. Only use explicit placement for items that break the pattern.
- Forgetting that
gapdoes not apply to edges: If you need padding around the grid, addpaddingto the container.gapis only between tracks.
Key Takeaways
- CSS Grid is a two-dimensional layout tool: rows and columns simultaneously
frunits distribute remaining space proportionally, likeflex-growfor gridrepeat(auto-fit, minmax(250px, 1fr))creates responsive grids without media queries- Grid template areas make complex layouts readable and maintainable
- Grid excels at page layouts, dashboards, and card grids; flexbox excels at component-level layout
- Grid items can overlap by placing them on the same grid lines
subgridsolves alignment issues in nested grids- Auto-placement handles most cases; use explicit placement only when needed