Skip to content
ZeroServer.tools
All guides

CSS Grid: The Complete Guide to Two-Dimensional Layouts

June 10, 2026 · 7 min read

CSS Grid: The Complete Guide to Two-Dimensional Layouts

CSS Grid is the most powerful layout system available in CSS. Unlike Flexbox, which works along a single axis at a time, Grid lets you control rows and columns simultaneously — making it ideal for full page layouts, dashboards, magazine-style content, and complex card arrangements.

This guide covers everything from the basics to advanced techniques including named grid areas, responsive layouts without media queries, and the holy grail layout in under 20 lines of CSS. Use the CSS Grid Generator to experiment visually, and check out the CSS Flexbox Guide to understand when to use Grid vs Flexbox.

Enabling Grid: display: grid

Apply display: grid to a container. Direct children become grid items placed in the resulting grid cells.

.container {
  display: grid;
}

On its own, this creates a single-column grid. You define structure with grid-template-columns and grid-template-rows.

grid-template-columns and grid-template-rows

These properties define the track sizes in your grid.

.container {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;  /* 3 columns: fixed, flexible, flexible */
  grid-template-rows: 80px auto 60px;    /* 3 rows: header, content, footer */
}

You can mix units freely: px, %, em, fr, auto, and minmax().

The fr Unit

The fr unit represents a fraction of the available space after fixed-size tracks are placed. It is the core of Grid's flexibility.

.container {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  /* total 4fr: first column gets 25%, second gets 50%, third gets 25% */
}

Think of fr as the "remaining space ratio." It only applies to leftover space after fixed widths are allocated.

repeat(): Avoiding Repetition

Instead of writing 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr, use repeat():

.container {
  display: grid;
  grid-template-columns: repeat(12, 1fr); /* 12-column grid */
  gap: 16px;
}

repeat() accepts a count and a track definition (or multiple tracks):

grid-template-columns: repeat(3, 100px 1fr); /* repeats "100px 1fr" 3 times = 6 columns */

gap: Space Between Tracks

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;           /* equal row and column gap */
  gap: 16px 24px;      /* row gap, column gap */
  row-gap: 16px;
  column-gap: 24px;
}

auto-fill vs auto-fit: Responsive Grids Without Media Queries

These keywords, combined with minmax(), let you build responsive grids that adapt automatically to viewport width.

/* auto-fill: creates as many columns as fit, even if empty */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
}

/* auto-fit: collapses empty columns, stretching filled ones to fill space */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
}

The difference: with auto-fill, empty columns retain their space. With auto-fit, they collapse and items expand to fill the row. For most card grids, auto-fit is what you want.

minmax(): Setting Track Boundaries

minmax(min, max) defines a range for a track size. This is what makes auto-fit/auto-fill responsive.

grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
/* Each column is at least 200px, but grows to fill available space */

You can also use it for rows:

grid-template-rows: repeat(3, minmax(100px, auto));
/* Each row is at least 100px, expands to fit content */

Spanning Columns and Rows

Items can span multiple tracks using grid-column and grid-row:

.item {
  grid-column: 1 / 3;     /* starts at line 1, ends at line 3 (spans 2 columns) */
  grid-row: 1 / 2;
}

/* Shorthand with span keyword */
.featured {
  grid-column: span 2;    /* spans 2 columns from wherever it's placed */
  grid-row: span 3;
}

/* Full width */
.banner {
  grid-column: 1 / -1;   /* -1 = last grid line */
}

Named Grid Areas

Named areas make complex layouts readable. Define a visual map of your layout as a string:

.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: 64px 1fr 48px;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
}

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

Use a . (dot) for empty cells:

grid-template-areas:
  "header header header"
  "sidebar main   ."
  "footer footer footer";

Real Example: The Holy Grail Layout

Header, sidebar, main content, and footer — in 18 lines of CSS, fully responsive.

.holy-grail {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  min-height: 100vh;
  gap: 0;
}

.header  { grid-area: header; padding: 16px 24px; }
.sidebar { grid-area: sidebar; padding: 24px 16px; }
.main    { grid-area: main; padding: 24px; }
.footer  { grid-area: footer; padding: 16px 24px; }

@media (max-width: 640px) {
  .holy-grail {
    grid-template-columns: 1fr;
    grid-template-areas:
      "header"
      "main"
      "sidebar"
      "footer";
  }
}

Real Example: Magazine Layout

A mixed-density card layout where the first card is featured (spans 2 columns and 2 rows):

.magazine {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 240px;
  gap: 16px;
}

.featured {
  grid-column: span 2;
  grid-row: span 2;
}
<div class="magazine">
  <article class="featured">Featured Story</article>
  <article>Story 2</article>
  <article>Story 3</article>
  <article>Story 4</article>
  <article>Story 5</article>
</div>

Real Example: Responsive Card Grid Without Media Queries

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 24px;
  padding: 24px;
}

.card {
  border-radius: 8px;
  border: 1px solid #e2e8f0;
  padding: 20px;
}

On a wide screen you might get 4 columns. On a tablet, 2 or 3. On mobile, 1. Zero media queries.

Real Example: 12-Column Grid System

The classic Bootstrap-style grid, but in 4 lines of CSS:

.row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 16px;
}

/* Usage */
.col-4  { grid-column: span 4; }
.col-6  { grid-column: span 6; }
.col-12 { grid-column: span 12; }

grid-auto-flow: Controlling Implicit Placement

When items aren't explicitly placed, Grid auto-places them. grid-auto-flow controls the direction:

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-flow: row;    /* default: fill row by row */
  grid-auto-flow: column; /* fill column by column */
  grid-auto-flow: dense;  /* backfill gaps when items span multiple tracks */
}

dense is useful for Pinterest-style layouts where items vary in height — it tries to fill holes in the grid.

Grid vs Flexbox: When to Use Which

Scenario Use
Single row of items (nav, toolbar) Flexbox
Single column (list, stack) Flexbox
Items should size themselves Flexbox
Full page layout Grid
Two-dimensional structure Grid
Items need to align across rows Grid

In practice, you'll use both. A Grid handles the overall page structure; Flexbox handles the contents within each grid area (like a nav bar inside the header).

Common Gotchas

Gotcha 1: Grid lines vs track indices. A 3-column grid has 4 column lines (1, 2, 3, 4). grid-column: 1 / 4 spans all 3 columns. New developers often write 1 / 3 and wonder why the last column is missing.

Gotcha 2: Implicit rows have no size by default. If you add more items than your defined rows can hold, new rows are created with auto height. Use grid-auto-rows to control implicit row sizing.

Gotcha 3: fr doesn't prevent overflow. In a grid with grid-template-columns: 1fr 1fr, if one item has min-width: 400px, it will overflow its 1fr column. Use minmax(0, 1fr) instead of 1fr when content might overflow.

Gotcha 4: Named areas must form a rectangle. You can't create L-shaped named areas. If your grid-template-areas string doesn't form valid rectangles for every name, the entire declaration is invalid.

Building Layouts Visually

Use the CSS Grid Generator to drag-and-drop your grid structure, assign areas, and copy production-ready CSS. It's far faster than counting grid lines by hand.

Once you understand Grid's two-dimensional power, revisit CSS Flexbox with fresh eyes — you'll find the two tools complement each other perfectly for almost any layout challenge.