grid-template-areas.
Readable grid layouts.
The most readable grid technique in CSS. Instead of counting columns and rows, you draw the layout with strings: 'header header' 'sidebar main' 'footer footer'. Then you assign elements to named regions with grid-area. The CSS looks like the layout it produces. This page covers the syntax, the dot-for-empty-cell trick, and the responsive pattern that makes it worth learning.
Short answer: Define named regions on the grid container with grid-template-areas — each row is a string, each name is a cell, repeated names merge. Assign children to regions with grid-area: name. Use . for empty cells. Redefine the template at a media query to change the layout responsively — the children don't change, only the template. This is the most readable way to write page layouts in CSS grid.
✅ The basic pattern
Draw the layout with strings.
.layout {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr 80px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 1rem;
min-height: 100vh;
}
.header { grid-area: header; background: #2965f1; }
.sidebar { grid-area: sidebar; background: #64748b; }
.main { grid-area: main; background: #f1f5f9; color: #1e293b; }
.footer { grid-area: footer; background: #334155; }
Visual layout: ┌──────────────────────────────┐ │ header │ 64px ├──────────┬───────────────────┤ │ │ │ │ sidebar │ main │ 1fr │ 240px │ auto │ │ │ │ ├──────────┴───────────────────┤ │ footer │ 80px └──────────────────────────────┘ Read the CSS at the top: "header header" → header spans 2 columns "sidebar main" → sidebar + main side by side "footer footer" → footer spans 2 columns The CSS matches the layout visually. No counting columns. No grid-column: 1 / 3. Just names. ✅ Easy to read. ✅ Easy to modify — change the strings. ✅ Easy to make responsive — redefine at a media query.
✅ Responsive — the big win
Redefine the template. Children don't change.
.layout {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
@media (min-width: 768px) {
.layout {
grid-template-columns: 240px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}
Mobile (< 768px): ┌───────────────────┐ │ header │ ├───────────────────┤ │ main │ ├───────────────────┤ │ sidebar │ ├───────────────────┤ │ footer │ └───────────────────┘ Desktop (≥ 768px): ┌──────────────────────────────┐ │ header │ ├──────────┬───────────────────┤ │ sidebar │ main │ ├──────────┴───────────────────┤ │ footer │ └──────────────────────────────┘ ✅ Sidebar moves below main on mobile. ✅ Header and footer span full width on desktop. ✅ One template per breakpoint. ✅ .sidebar, .main etc. never change — their grid-area is the same name everywhere. This is the cleanest responsive grid technique. Change the strings, everything follows.
✅ Empty cells and spans
Use dots for gaps.
.page {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-areas:
"hero hero hero hero"
"promo . sidebar sidebar"
"footer footer footer footer";
gap: 1rem;
}
.hero { grid-area: hero; background: #2965f1; }
.promo { grid-area: promo; background: #f59e0b; }
.sidebar { grid-area: sidebar; background: #10b981; }
.footer { grid-area: footer; background: #334155; }
Visual layout: ┌──────────────────────────────────┐ │ hero │ ├──────────┬─────┬─────────────────┤ │ promo │ . │ sidebar │ ├──────────┴─────┴─────────────────┤ │ footer │ └──────────────────────────────────┘ .promo occupies column 1 of row 2. . (dot) marks the empty cell in column 2. .sidebar spans columns 3-4 of row 2. ✅ hero spans all 4 columns. ✅ promo is 1 column. ✅ Dot creates the gap. ✅ sidebar is 2 columns. ✅ footer spans all 4. The dot is how you leave space in a named grid. Without it, the grid would fill every cell. Rule: one dot = one empty cell. Repeat as needed. Multiple dots (.... ) is convention for readability but only one is required.
Don't use grid-template-areas without also defining grid-template-columns. The template areas define the shape of the grid, but the columns need explicit widths. Without grid-template-columns, the grid defaults to one auto-sized column per cell — which may be what you want, but usually isn't. Combine them: grid-template-columns: 240px 1fr sets the widths, grid-template-areas sets the layout. Both are needed. If you only set grid-template-areas, the grid still works, but the columns are sized by content, not by your design.
⚠️ Common mistakes
The grid-template-areas mistakes that cost real time.
'header header' and 'sidebar' — different cell counts. Fix: every row must have the same number of cells.
grid-area: Header vs template 'header'. Case-sensitive. Fix: match exactly.
Only direct children of the grid container accept grid-area. Fix: move the element or use a nested grid.
The grid uses auto-sized columns. Fix: define column widths explicitly.
grid-area: 'my header' — invalid. Fix: no spaces in area names.
The element becomes auto-placed. Fix: ensure the name is in the template strings.
grid-template-rows defaults to auto — usually fine, but explicit rows help with fixed heights.
A 6×6 template for a simple layout. Fix: only name the regions you need; use grid-column/row for the rest.
❓ Frequently asked
What is grid-template-areas?
grid-template-areas is a CSS grid property that lets you name regions of a grid using an ASCII-art string. Each string is a row, each name in the string is a cell, and repeated names merge into a single region. Example: grid-template-areas: 'header header' 'sidebar main' 'footer footer'. Then .header { grid-area: header; } places the element in that region.
How does grid-area work with template areas?
grid-area:
Can I have empty cells in grid-template-areas?
Yes — use a dot (.) for an empty cell. grid-template-areas: 'header header' 'sidebar .' 'footer footer' — the middle-right cell is empty. Use multiple dots (...) for wider gaps. Empty cells let you create L-shaped regions and gaps within a grid. The dot is not a name — it means 'no grid-area here'.
Can grid-template-areas be responsive?
Yes — this is where it shines. Redefine the whole grid at a media query by changing the strings: grid-template-areas: 'header' 'main' 'sidebar' 'footer' on mobile, then 'header header' 'sidebar main' 'footer footer' on desktop. The child elements don't change — they keep grid-area: header, grid-area: sidebar, etc. Only the template strings change.
Why doesn't my grid-area work?
Four common causes. (1) The name doesn't match — 'Header' vs 'header', case-sensitive. (2) The grid container doesn't have grid-template-areas defined. (3) The element is not a direct child of the grid. (4) The template strings have different lengths. Fix each at the source.
When should I use grid-template-areas vs grid-column/grid-row?
Use grid-template-areas when the layout has named, rectangular regions — page layouts, dashboards, card grids with featured items. Use grid-column/grid-row when the layout is more dynamic or irregular — when cells have complex spans, when the grid is auto-generated. For most page-level layouts, grid-template-areas is the better tool.