Skip to main content

CSS Grid

CSS Grid is a two-dimensional layout system. While Flexbox handles rows or columns, Grid handles rows and columns at the same time. It is the most powerful layout tool in CSS.

When to use Grid vs Flexbox

ScenarioUse
Items in a single row or columnFlexbox
Aligning items along one axisFlexbox
A full page layout with rows + columnsGrid
Cards in a responsive gridGrid
Both rows and columns matterGrid

In practice, you often use both together: Grid for the overall page layout, Flexbox for smaller components inside it.

Grid container and grid items

Set display: grid on a container. Its direct children become grid items:

.grid {
display: grid;
}

By default, grid items stack vertically (one per row), just like block elements. You need to define columns and rows to see the grid effect.

Defining columns and rows

grid-template-columns

Defines the number and width of columns:

.grid {
display: grid;
grid-template-columns: 200px 200px 200px;
}

This creates three columns, each 200px wide.

grid-template-rows

Defines the number and height of rows:

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

Two rows, each 100px tall.

The fr unit

The fr (fraction) unit distributes available space proportionally. It is the most important unit in Grid:

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

Three equal-width columns that fill all available space. 1fr 2fr 1fr gives the middle column twice the width of the side columns.

You can mix fr with fixed units:

.layout {
display: grid;
grid-template-columns: 250px 1fr;
}

A fixed 250px sidebar and a main content area that takes the remaining space.

The repeat() function

Shorthand for repeating track definitions:

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

Equivalent to 1fr 1fr 1fr. You can repeat any pattern:

grid-template-columns: repeat(4, 100px 1fr);
/* = 100px 1fr 100px 1fr 100px 1fr 100px 1fr */

Gap

Just like Flexbox, gap adds space between grid items:

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

Set row and column gaps separately:

.grid {
row-gap: 24px;
column-gap: 16px;
}

Placing items

Grid items automatically fill cells left to right, top to bottom. You can override this with placement properties.

Line-based placement

Grid lines are numbered starting at 1. Use grid-column and grid-row to place items:

.header {
grid-column: 1 / 4;
}

This means: start at column line 1, end at column line 4 -- spanning all three columns in a 3-column grid.

.sidebar {
grid-column: 1 / 2;
grid-row: 2 / 4;
}

The sidebar occupies column 1 and spans rows 2 through 3.

The span keyword

Instead of specifying end lines, you can use span:

.header {
grid-column: span 3;
}

.feature {
grid-column: span 2;
grid-row: span 2;
}

span 3 means "occupy three columns from wherever the item is placed."

Grid template areas

For complex layouts, named areas are the most readable approach:

.layout {
display: grid;
grid-template-columns: 250px 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; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
<div class="layout">
<header class="header">Header</header>
<aside class="sidebar">Sidebar</aside>
<main class="main">Main content</main>
<footer class="footer">Footer</footer>
</div>

The grid-template-areas property draws a visual map of the layout. Each string is a row; each word is a cell. Repeat a name to span multiple cells. Use a period (.) for empty cells:

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

Tip: Grid template areas are excellent for page-level layouts. The visual representation in the CSS directly mirrors the visual result, making the code self-documenting.

Alignment in Grid

Grid supports the same alignment properties as Flexbox, plus a few extras.

Container-level alignment

PropertyAxisWhat it aligns
justify-itemsInlineAll items within their cells (horizontal)
align-itemsBlockAll items within their cells (vertical)
justify-contentInlineThe entire grid within the container
align-contentBlockThe entire grid within the container
.grid {
display: grid;
grid-template-columns: repeat(3, 200px);
justify-content: center;
align-items: center;
min-height: 400px;
}

Item-level alignment

PropertyAxisWhat it aligns
justify-selfInlineA single item within its cell
align-selfBlockA single item within its cell
.special {
justify-self: end;
align-self: start;
}

The place shorthand

place-items, place-content, and place-self combine the align and justify versions:

.grid {
place-items: center;
}

Responsive grids with auto-fit and auto-fill

The most powerful responsive Grid pattern uses auto-fit or auto-fill with minmax():

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

This creates a responsive grid where:

  • Each column is at least 280px wide
  • Columns expand to fill available space (up to 1fr)
  • The browser automatically creates as many columns as fit
  • No media queries needed

auto-fit vs auto-fill

KeywordBehaviour when there are few items
auto-fitCollapses empty tracks, items stretch to fill
auto-fillKeeps empty tracks, items stay at minimum size

In most cases, auto-fit is what you want.

minmax()

The minmax(min, max) function sets a minimum and maximum size for a grid track:

grid-template-columns: minmax(200px, 1fr) 3fr;

The first column is at least 200px but can grow up to 1fr. The second column gets 3fr.

Implicit vs explicit grid

The explicit grid is what you define with grid-template-columns and grid-template-rows. If items overflow past your defined tracks, the browser creates implicit tracks automatically.

Control the size of implicit tracks:

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

grid-auto-rows: 150px makes any extra rows 150px tall. Without it, implicit rows auto-size to fit their content.

Use minmax() for flexible implicit rows:

.grid {
grid-auto-rows: minmax(100px, auto);
}

Practical examples

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

.gallery img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 4px;
}

Holy Grail layout

The classic header/sidebar/main/footer layout:

.page {
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;
}

.page > header { grid-area: header; padding: 16px; background: #1a1a2e; color: white; }
.page > aside { grid-area: sidebar; padding: 16px; background: #f5f5f5; }
.page > main { grid-area: main; padding: 24px; }
.page > footer { grid-area: footer; padding: 16px; background: #333; color: white; }

Dashboard with different-sized cards

.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 180px;
gap: 16px;
}

.dashboard .wide {
grid-column: span 2;
}

.dashboard .tall {
grid-row: span 2;
}

.dashboard .large {
grid-column: span 2;
grid-row: span 2;
}

What you learned

  • CSS Grid is two-dimensional -- it handles rows and columns
  • grid-template-columns and grid-template-rows define the grid structure
  • The fr unit distributes space proportionally; repeat() avoids repetition
  • gap adds space between tracks
  • Items can be placed by line numbers (grid-column: 1 / 3), by span (span 2), or by named areas
  • auto-fit with minmax() creates responsive grids without media queries
  • Implicit tracks handle overflow items automatically

Next step

Grid and Flexbox handle layout. The next chapter covers responsive design -- media queries, breakpoints, and mobile-first development to make your layouts work on every screen size.