CSS Grid Tutorial

May 28, 20266 min read

Getting Started with CSS Grid

CSS Grid is a two-dimensional layout system that controls both rows and columns simultaneously. It replaces float-based layouts, table hacks, and inline-block workarounds with a declarative model the browser handles natively.

To activate Grid, set display: grid on a container. Its direct children become grid items.

.grid-container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

.grid-item {
  /* Each item occupies one cell by default */
}

Only direct children participate in the grid. Nested elements need their own display: grid if you want sub-grid behavior.

grid-template-columns and grid-template-rows

These properties define the track structure — the columns and rows of your grid.

Fixed Sizes

.grid-container {
  display: grid;
  grid-template-columns: 200px 300px 200px;
  grid-template-rows: 100px 400px;
}

Columns stay at exactly 200px, 300px, and 200px. Not responsive, but useful for fixed-width layouts.

Flexible Sizes with fr

The fr unit distributes remaining space proportionally:

.grid-container {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
}

The middle column gets twice the space of each outer column. This is the most common pattern for responsive grids.

repeat() Notation

Save keystrokes with repeat():

/* Equivalent declarations */
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-columns: repeat(4, 1fr);

/* With minmax */
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));

Mixing Units

Combine fixed and flexible tracks in one declaration:

.grid-container {
  display: grid;
  grid-template-columns: 260px 1fr 1fr;
  grid-template-rows: auto 1fr auto;
}

A fixed sidebar on the left, flexible content columns, and rows that size to content at the top and bottom.

grid-gap (Row and Column Gaps)

Gaps control spacing between tracks. They replace margin-based spacing and never apply before the first item or after the last.

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

Gap values are subtracted from available space before fr units are calculated. With a 1000px container, three 1fr columns, and a 24px gap, each column gets (1000 - 48) / 3 = 317px.

grid-area and Named Areas

grid-template-areas is the most readable way to define complex layouts. You draw the layout with names instead of calculating line numbers:

.dashboard {
  display: grid;
  grid-template-columns: 240px 1fr 1fr;
  grid-template-rows: 64px 1fr 1fr 48px;
  grid-template-areas:
    "nav     nav     nav"
    "sidebar chart   chart"
    "sidebar table   stats"
    "footer  footer  footer";
  gap: 16px;
}

.nav     { grid-area: nav; }
.sidebar { grid-area: sidebar; }
.chart   { grid-area: chart; }
.table   { grid-area: table; }
.stats   { grid-area: stats; }
.footer  { grid-area: footer; }

A period (.) marks an empty cell. Named areas make the layout visible in the CSS itself, easy to rearrange for mobile, and simple for teams to understand.

Shorthand: grid-area with Line Numbers

You can also use grid-area as a shorthand for all four placement values:

.featured-item {
  grid-area: 1 / 1 / 3 / 4;
  /* row-start / column-start / row-end / column-end */
}

grid-auto-flow

When items outnumber explicit tracks, the browser creates implicit tracks. grid-auto-flow controls how items fill those tracks:

ValueBehavior
row (default)Fills left to right, then wraps to next row
columnFills top to bottom, then moves to next column
denseBackfills gaps left by spanning items
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-auto-flow: dense;
  gap: 8px;
}

Use dense for image galleries where items have varying spans and you want compact layouts without empty gaps.

Practical Examples

Holy Grail Layout

The classic web layout — header, three-column body, and footer:

.holy-grail {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header  header"
    "left   content right"
    "footer footer  footer";
  min-height: 100vh;
}

All three columns maintain consistent height without JavaScript or table hacks.

Dashboard Layout

Asymmetrical panels with varying spans:

.dashboard {
  display: grid;
  grid-template-columns: 260px 1fr 1fr 1fr;
  grid-template-rows: 56px 2fr 1fr;
  grid-template-areas:
    "toolbar toolbar toolbar toolbar"
    "nav     main    main    aside"
    "nav     widget1 widget2 aside";
  gap: 12px;
}

The 2fr row gives the main area more vertical space than the widget row.

Responsive grid with featured items spanning multiple cells:

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-auto-rows: 200px;
  grid-auto-flow: dense;
  gap: 8px;
}

.gallery-item.featured {
  grid-column: span 2;
  grid-row: span 2;
}

The dense packing ensures smaller items fill gaps left by the featured images.

Spanning and Placement

Items can span multiple tracks using line numbers or the span keyword:

/* Span 2 columns */
.item {
  grid-column: span 2;
}

/* Span from line 2 to line 4 */
.item {
  grid-column: 2 / 4;
}

/* Span 2 columns starting from line 2 */
.item {
  grid-column: 2 / span 2;
}

/* Full shorthand: row-start / col-start / row-end / col-end */
.item {
  grid-area: 1 / 1 / 3 / 4;
}

Prefer span over raw line numbers when you do not need to target specific lines — it is more readable and resilient to track changes.

Alignment

Grid provides two levels of alignment:

PropertyScopeControls
justify-itemsContainerHorizontal alignment of all items
align-itemsContainerVertical alignment of all items
place-itemsContainerBoth axes (align / justify)
justify-selfItemOverride horizontal for one item
align-selfItemOverride vertical for one item
.grid-container {
  place-items: center;
}

.stretch-item {
  align-self: stretch;
}

Try It Yourself

Experiment with grid areas, column spans, and responsive patterns in our CSS Grid Generator. Adjust tracks visually and copy production-ready CSS.