How-To

How-To · Grid

How to Create a Grid Layout in CSS

Build a responsive grid layout step by step: turn a container into a grid, define column tracks with grid-template-columns, add a gap between them, and make the column count responsive with repeat() and minmax().

XCODXHow-To · Updated

Overview

To lay elements out in rows and columns, the modern approach is CSS Grid: turn a container into a grid, tell it how many columns you want, and add a gutter between the cells. You need it whenever a set of cards, tiles, or panels should sit in a tidy two-dimensional arrangement rather than a single line. This guide builds one the recommended way — display: grid, then grid-template-columns, then gap — and finishes by making the column count respond to the viewport.

Steps

1. Turn the container into a grid with display: grid

Start by promoting the wrapper element to a grid container. Setting display: grid makes every direct child a grid item; on its own it stacks the children in a single column, which is the blank canvas you will shape in the next step.

.grid {
  display: grid;
}

2. Define the columns with grid-template-columns

Now describe the column tracks. grid-template-columns takes a list of track sizes, and the number of sizes is the number of columns. Use the fr unit so the columns share the available width equally — repeat(3, 1fr) is a concise way to write three equal columns, and items flow into them left to right, wrapping to a new row when the row is full.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

3. Add space between cells with gap

A grid with no gutters looks cramped, so add a gap. Applied to the container, it inserts space between every row and column at once without adding margins to the items — and, importantly, it never adds space around the outer edge, so the grid still sits flush inside its parent.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

4. Make it responsive with repeat() and minmax()

A fixed count of three columns will squeeze on a phone. Swap the explicit count for repeat(auto-fit, minmax(140px, 1fr)): the grid now fits as many columns as the width allows, each at least 140px wide and stretching to fill the row. Columns are added and removed automatically as the viewport changes — a fully responsive grid with no media queries.

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1rem;
}

Runnable example

The complete, responsive result — resize the preview and watch the numbered cells reflow into more or fewer columns. Open it in the editor to tweak the track sizes, and if you only need a single row of items, reach for CSS Flexbox instead:

A fixed sidebar next to a fluid main column is the other layout you will reach for constantly — pin the first track to a pixel width and let 1fr soak up the rest:

Two more patterns worth keeping close. To make one cell span the full width — a header or footer that stretches across every column — set grid-column: 1 / -1 on it (-1 is the last grid line):

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.grid .full-width {
  /* start at the first line, end at the last */
  grid-column: 1 / -1;
}

And for a whole page skeleton, name the regions with grid-template-areas and place each child by name — the layout reads like a picture of itself:

.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  gap: 1rem;
}

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

Finally, to give rows a fixed height and center each item within its cell, add grid-template-rows and the place-items shorthand (which sets align-items and justify-items at once):

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 120px 120px;
  gap: 1rem;
  /* center content both ways inside every cell */
  place-items: center;
}

And a single tile can span several tracks — give it a start and an end on each axis with the span keyword:

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

.grid .feature {
  /* cover two columns and two rows */
  grid-column: span 2;
  grid-row: span 2;
}
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* let later items backfill the holes a span leaves */
  grid-auto-flow: dense;
  gap: 1rem;
}

Common pitfalls

One more nuance: with repeat(auto-fit, minmax(140px, 1fr)), a single leftover item stretches to fill the whole row. If you prefer it to stay one column wide, use auto-fill instead of auto-fitauto-fill keeps the empty track slots, so lone items do not expand.

Frequently asked questions

What is the simplest way to create a grid in CSS?
Set display: grid on the container, then grid-template-columns: repeat(3, 1fr) for three equal columns, and gap: 1rem for spacing. Three declarations give you a working grid.
How do I make a CSS grid responsive without media queries?
Use grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)). The grid fits as many columns as the width allows, each at least 140px, adding and removing columns automatically as the viewport changes.
Why is my grid showing only one column?
Usually because grid-template-columns is not set, or the items are not direct children of the grid container. Grid only positions direct children — an extra wrapper becomes a single item.
How do I add space between grid cells?
Use the gap property on the grid container, for example gap: 1rem. It spaces every row and column and never adds space around the outer edge, so prefer it over per-item margins.