CSS grid-template-areas

grid-template-areas is a CSS Grid property that lets you name regions of a grid layout and then draw the layout as a simple text picture, right inside your stylesheet. Instead of placing items with row and column numbers, you give each area a name (like header, sidebar, or main) and assign elements to those names with grid-area. The result is a layout that reads almost like a diagram, making complex page structures easy to write, easy to scan, and easy to change.

Overview / How it works

grid-template-areas only works on an element that is already a grid container (display: grid or display: inline-grid). Once a container is a grid, the browser’s layout engine divides it into rows and columns based on grid-template-columns and grid-template-rows (or the implicit grid, if you don’t define them). grid-template-areas then overlays names onto that grid of cells, one row per quoted string, one name per cell. Each name in the string must repeat to form a solid rectangle for it to be valid — grid areas can never be L-shaped or have a gap in the middle.

Once the named grid exists, individual grid items are assigned to a named region with the shorthand property grid-area: <name> (this is the same property used for row/column-based placement, just given a name instead of numbers). The rendering engine resolves the string grid into row-start/row-end/column-start/column-end line numbers internally — grid-template-areas is really just a friendlier syntax layered on top of the same line-based placement algorithm every other grid property uses. That’s also why you can freely mix named-area items with items placed via grid-column/grid-row in the same grid; they all resolve to the same underlying line coordinates.

A period (.) is a special token meaning “leave this cell empty” — no item is required to fill it, and the grid simply shows blank space (or whatever background the container has) there. This makes it easy to reserve empty space in a layout without inventing a dummy element.

Syntax

.container {
  display: grid;
  grid-template-areas:
    "area-name-1 area-name-2"
    "area-name-3 area-name-4";
}

.item {
  grid-area: area-name-1;
}
Part Meaning
Each quoted string Represents one row of the grid
Each word inside a string Represents one column-cell in that row, named after a grid item’s assigned area
Repeating a name across cells Merges those cells into one larger area (must stay rectangular)
. (a single period) Marks that cell as an empty, unnamed grid cell
grid-area on a child Assigns that child element to a named area declared in the parent’s grid-template-areas

Every row string must contain the same number of cell-names, and the whole declaration typically pairs with grid-template-columns/grid-template-rows to control the actual track sizes — the strings only describe naming and shape, not pixel widths.

Examples

Example 1: A classic page shell. Consider a page with a full-width header, a fixed sidebar, a flexible main content column, and a full-width footer. Assume the HTML has a .page wrapper containing a header, a nav, a main, and a footer.

.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  min-height: 100vh;
  gap: 12px;
}

.site-header { grid-area: header; }
.site-nav    { grid-area: sidebar; }
.site-main   { grid-area: main; }
.site-footer { grid-area: footer; }

Result: The header spans both columns at the top, the footer spans both columns at the bottom, and between them the sidebar occupies a fixed 200px-wide column while the main content fills the remaining space. The whole layout stretches to at least the height of the viewport, with 12px gaps between every region.

Notice that header appears twice in the first row string — that’s what makes the header span both the sidebar and main columns rather than only occupying one cell.

Example 2: A dashboard with an empty cell and a responsive redesign.

.dashboard {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-areas:
    "title title title title"
    "stats stats . chart"
    "table table table chart";
  gap: 16px;
}

.dash-title { grid-area: title; }
.dash-stats { grid-area: stats; }
.dash-chart { grid-area: chart; }
.dash-table { grid-area: table; }

@media (max-width: 700px) {
  .dashboard {
    grid-template-columns: 1fr;
    grid-template-areas:
      "title"
      "stats"
      "chart"
      "table";
  }
}

Result: On wide screens the title banner spans all four columns, the stats box occupies two columns in the second row with one empty cell beside it, and the chart spans two rows on the right. Under 700px wide, the whole layout collapses to a single column stacked in the order title, stats, chart, table — with no HTML changes needed, just a redefinition of grid-template-areas and grid-template-columns inside the media query.

This is one of the most powerful traits of named areas: because the mapping from element to area name never changes (each element always declares the same grid-area), you can completely restructure the visual order of a layout at different breakpoints by only rewriting the parent’s area strings.

Example 3: Combining areas, rows, and columns in one shorthand. The grid-template shorthand can define areas, row sizes, and column sizes together for a compact card component.

.card {
  display: grid;
  grid-template:
    "img title"   auto
    "img desc"    1fr
    "img actions" auto
    / 120px 1fr;
  gap: 8px 16px;
}

.card-img     { grid-area: img; }
.card-title   { grid-area: title; }
.card-desc    { grid-area: desc; }
.card-actions { grid-area: actions; }

Result: A fixed 120px image column runs down the full height of the card on the left. On the right, the title sits at auto height, the description fills the remaining flexible space, and the action buttons sit at auto height below it — three stacked rows all sharing the same right-hand column.

How it works step by step

When the rendering engine encounters grid-template-areas, it performs roughly these steps:

1. Parse the strings. Each quoted string becomes one grid row; each whitespace-separated token in the string becomes one column cell in that row.

2. Validate the shape. Every occurrence of a given name must form a single, unbroken rectangle. If a name appears in a non-rectangular pattern, the whole grid-template-areas declaration is treated as invalid and ignored by the browser.

3. Compute implicit line names. For every named area, the engine automatically generates grid line names like header-start and header-end, which is why you can still use line-based properties like grid-column alongside named areas if needed.

4. Merge with explicit tracks. The named grid is combined with whatever grid-template-columns/grid-template-rows specify, producing the final set of row and column tracks with real sizes.

5. Place items. Any child with a matching grid-area value is placed into the rectangle of cells that share that name — internally converted to the same start/end line numbers used by manual placement.

6. Lay out remaining items. Grid items without a matching name (or without any explicit placement) flow into the implicit grid using the container’s auto-placement algorithm, filling leftover cells such as those marked with ..

Common Mistakes

Mistake 1: Rows with a different number of cells. Every row string in a grid-template-areas declaration must describe the same number of columns. Mismatched row lengths make the whole declaration invalid.

.layout {
  display: grid;
  grid-template-areas:
    "header header"
    "sidebar main extra"
    "footer footer";
}

The second row lists three cells while the first and third list only two, so the browser discards the entire property. Fix it by keeping every row the same length, using . for any cell that should stay empty:

.layout {
  display: grid;
  grid-template-areas:
    "header header ."
    "sidebar main extra"
    "footer footer footer";
}

Mistake 2: A typo between the area name and grid-area. Because names are just plain identifiers, a misspelling silently fails instead of throwing an error — the item simply falls back to auto-placement.

.layout {
  display: grid;
  grid-template-areas:
    "header header"
    "sidebar main";
}

.site-main { grid-area: mian; }

Here mian doesn’t match any name declared in grid-template-areas, so .site-main ignores the named grid and gets auto-placed into the next available implicit cell instead of the intended main region. Always double check that every grid-area value exactly matches a name spelled in the parent’s area strings:

.site-main { grid-area: main; }

Best Practices

  • Format each row of grid-template-areas on its own line and align columns visually — the whole point of this property is that the CSS itself reads like a diagram of the layout.
  • Keep area names short, lowercase, and hyphen-free where possible (nav, main, aside) so rows line up cleanly and are easy to scan.
  • Use . for intentionally empty cells rather than inventing placeholder elements just to fill space.
  • Pair grid-template-areas with grid-template-columns/grid-template-rows (or the grid-template shorthand) so track sizes are explicit rather than left to default sizing.
  • Redefine grid-template-areas inside media queries to reorder a layout responsively without touching the HTML or the grid-area declarations on individual elements.
  • Avoid reusing the same area name in a non-rectangular shape; if a region needs an irregular footprint, split it into two named areas instead.

Practice Exercises

Exercise 1: Build a grid container with three named areas — logo, search, and account — arranged in a single header row, where search takes up twice the width of the other two. Use grid-template-columns alongside grid-template-areas to achieve the width ratio.

Exercise 2: Create a three-column, two-row grid for a blog post layout with areas title, meta, content, and related, where title spans all three columns on top, and content takes up two columns while related takes the remaining column in the second row. Then write a media query that collapses everything to a single stacked column below 600px.

Exercise 3: Take the mistake example with mismatched row lengths from this lesson and rewrite it so all three rows have exactly three cells each, deciding for yourself where . empty cells belong versus where existing names should expand.

Summary

  • grid-template-areas assigns names to rectangular regions of a grid container, drawn as quoted row strings.
  • Child elements join a named region using the matching grid-area value.
  • A period (.) marks a cell as intentionally empty.
  • Every row string must have the same number of cell names, and each name must form one unbroken rectangle, or the whole declaration is invalidated.
  • Named areas resolve internally to the same line-number placement system used elsewhere in Grid, so they can be mixed freely with line-based placement.
  • Redeclaring grid-template-areas inside a media query is a powerful, HTML-free way to reorder a responsive layout.