CSS Grid Layout: The Complete Guide for 2026
CSS Grid is the most powerful layout system available in CSS. It is a two-dimensional system, meaning it can handle both columns and rows simultaneously, unlike Flexbox which operates in one direction at a time. Grid lets you define the structure of your layout in CSS, then place items precisely into that structure. Whether you are building a full page layout, a responsive card grid, a dashboard with mixed-size widgets, or any arrangement where you need explicit control over rows and columns, CSS Grid is the tool you want.
This guide covers everything you need to know to use CSS Grid confidently in production: the fundamentals of grid containers and items, all the sizing functions, item placement, named areas, alignment, responsive patterns that work without media queries, and complete real-world layout examples you can copy and adapt. If you have been using Flexbox for everything or still relying on float-based hacks, this is your roadmap to modern layout.
What Is CSS Grid and Why Use It?
CSS Grid Layout (commonly called CSS Grid) is a layout system that lets you divide a page or component into rows and columns, then place child elements into specific cells or regions of that grid. It was designed from the ground up for two-dimensional layout, which is what distinguishes it from every other CSS layout method.
Before Grid existed, developers used floats, inline-block, tables, and eventually Flexbox to build layouts. Each of these approaches has limitations. Floats require clearfixes and cannot vertically center content. Tables tie your layout to your HTML structure. Flexbox works in one dimension, so building a true two-dimensional layout requires nesting multiple flex containers and hoping they line up. Grid eliminates these workarounds entirely.
Here is what makes CSS Grid uniquely powerful:
- Two-dimensional control — define both columns and rows in a single declaration, and place items anywhere in that structure
- Explicit placement — put any item in any cell, regardless of its order in the HTML
- Named template areas — describe your layout visually in CSS with human-readable names
- Intrinsic sizing — use
frunits,minmax(),auto-fit, andauto-fillfor flexible, responsive layouts - Item spanning — let items stretch across multiple rows and/or columns
- Overlap support — items can occupy the same cells, controlled with
z-index - Gap control — built-in spacing between cells with the
gapproperty
The mental model for Grid is straightforward: you define a structure (columns and rows), and then you place items into that structure. The structure comes first, the content adapts to it. This is the opposite of Flexbox, where content drives the layout.
Grid Container vs Grid Items
Every CSS Grid layout has two players: the grid container and the grid items. The container is the parent element where you declare display: grid. The items are the direct children of that container.
<div class="grid-container"> <!-- Grid container -->
<div class="item">A</div> <!-- Grid item -->
<div class="item">B</div> <!-- Grid item -->
<div class="item">C</div> <!-- Grid item -->
<div class="item">D</div> <!-- Grid item -->
</div>
.grid-container {
display: grid;
/* Container properties go here:
grid-template-columns, grid-template-rows,
grid-template-areas, gap, justify-items,
align-items, etc. */
}
.item {
/* Item properties go here:
grid-column, grid-row, grid-area,
justify-self, align-self, etc. */
}
Only direct children become grid items. Grandchildren and deeper descendants are not part of the grid and lay out normally within their parent grid item. If you need a nested grid, apply display: grid to the child element as well, or use the newer subgrid feature to inherit the parent's track definitions.
A key concept: grid items are placed into grid cells, which are the intersections of rows and columns. The lines between cells are called grid lines, numbered starting from 1. You reference these line numbers when placing items explicitly.
/* Grid line numbers for a 3-column, 2-row grid:
Column lines: 1 2 3 4
| | | |
Row line 1 ── ┌──────┬──────┬──────┐
│ A │ B │ C │
Row line 2 ── ├──────┼──────┼──────┤
│ D │ E │ F │
Row line 3 ── └──────┴──────┴──────┘
Item A occupies: column 1-2, row 1-2
Item C occupies: column 3-4, row 1-2 */
Defining Columns and Rows
The grid-template-columns and grid-template-rows properties define the track sizes of your grid. A "track" is a column or row. You list the sizes separated by spaces, and each value creates one track.
Fixed Sizes: px, rem, em
/* Three columns: 200px, 400px, 200px */
.grid {
display: grid;
grid-template-columns: 200px 400px 200px;
}
Fixed sizes are useful for sidebars, fixed-width navigation panels, or any column that should not change size regardless of the viewport.
Flexible Sizes: The fr Unit
The fr (fraction) unit is the most important sizing unit in Grid. It distributes remaining space proportionally after fixed-size tracks have been accounted for.
/* Three equal columns */
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
/* Sidebar (fixed) + content (flexible) */
.grid {
display: grid;
grid-template-columns: 250px 1fr;
/* The sidebar is always 250px.
The content column takes all remaining space. */
}
/* 1:2:1 ratio */
.grid {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
/* The middle column is twice as wide as the outer columns. */
}
The fr unit is like flex-grow in Flexbox, but for grid tracks. If you have 1fr 2fr 1fr, the total is 4fr. The first column gets 1/4 of the space, the second gets 2/4 (half), and the third gets 1/4.
Percentage Sizes
.grid {
display: grid;
grid-template-columns: 25% 50% 25%;
}
Percentages work but have a catch: they are based on the container's width, and they do not account for gaps. If you have 25% 50% 25% with a gap of 1rem, the total exceeds 100% and causes overflow. The fr unit avoids this problem because it distributes space after gaps are subtracted. Prefer fr over percentages in most cases.
auto Sizing
.grid {
display: grid;
grid-template-columns: auto 1fr auto;
}
The auto keyword sizes a track to fit its content. In the example above, the first and last columns shrink to wrap their content, and the middle column absorbs all remaining space. This is the classic pattern for a header with a logo on the left, navigation in the center, and a button on the right.
minmax(): Setting Size Boundaries
The minmax() function defines a size range for a track. It takes two arguments: a minimum and a maximum.
.grid {
display: grid;
grid-template-columns: minmax(200px, 300px) 1fr minmax(200px, 300px);
/* Side columns are at least 200px and at most 300px.
The center column takes the remaining space. */
}
/* Rows that grow with content but have a minimum height */
.grid {
display: grid;
grid-template-rows: minmax(100px, auto);
/* Each row is at least 100px, but grows taller if content requires it. */
}
The most common use of minmax() is in the responsive grid pattern with auto-fill, which we will cover in the auto-fit and auto-fill section below.
repeat(): Reducing Repetition
The repeat() function lets you define repeating track patterns without writing the same value multiple times.
/* These two are identical: */
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-columns: repeat(4, 1fr);
/* Repeat a pattern: */
grid-template-columns: repeat(3, 100px 1fr);
/* Expands to: 100px 1fr 100px 1fr 100px 1fr (6 columns) */
/* Combine with other values: */
grid-template-columns: 200px repeat(3, 1fr) 200px;
/* 200px | 1fr | 1fr | 1fr | 200px */
Defining Rows
The grid-template-rows property works identically to grid-template-columns, but for horizontal tracks:
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 80px 1fr 60px;
/* Row 1 (header): fixed 80px
Row 2 (content): fills remaining vertical space
Row 3 (footer): fixed 60px */
min-height: 100vh;
}
If you do not define grid-template-rows, rows are created implicitly and sized to fit their content (the default is auto).
Grid Gaps
The gap property (formerly grid-gap) controls the spacing between grid cells. It does not add space on the outer edges of the grid, only between tracks.
/* Equal gap in both directions */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
/* Different row and column gaps */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
row-gap: 2rem;
column-gap: 1rem;
}
/* Shorthand: row-gap then column-gap */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem 1rem; /* 2rem between rows, 1rem between columns */
}
The gap property is one of the key advantages of Grid over older layout methods. With floats, you had to use margins and then compensate for the extra margin on the first or last item. With Grid, gap is pure spacing between cells with no edge effects to worry about.
Placing Items: grid-column and grid-row
By default, grid items fill cells in order: left to right, top to bottom (or following the writing direction). But you can place any item in any cell, or make it span multiple cells, using grid-column and grid-row.
Placing by Line Numbers
.item {
grid-column: 2 / 4; /* Start at column line 2, end at column line 4 */
grid-row: 1 / 3; /* Start at row line 1, end at row line 3 */
}
/* This item spans columns 2-3 and rows 1-2:
Col: 1 2 3 4
| | | |
1 ── ┌──────╔══════╦══════╗
│ ║ item ║ item ║
2 ── ├──────╠══════╬══════╣
│ ║ item ║ item ║
3 ── └──────╚══════╩══════╝ */
Using the span Keyword
Instead of specifying start and end lines, you can tell an item how many tracks to span:
/* Span 2 columns starting from wherever the item is placed */
.item {
grid-column: span 2;
}
/* Start at column 1, span 3 columns */
.item {
grid-column: 1 / span 3;
}
/* Span 2 rows */
.item {
grid-row: span 2;
}
/* Span both directions — a 2x2 block */
.large-item {
grid-column: span 2;
grid-row: span 2;
}
Negative Line Numbers
Negative line numbers count from the end of the explicit grid. -1 is the last line, -2 is the second-to-last, and so on.
/* Span the full width of a 3-column grid */
.full-width {
grid-column: 1 / -1;
/* In a 3-column grid, -1 equals line 4 */
}
/* Last two columns */
.right-side {
grid-column: -3 / -1;
}
This is extremely useful for items like headers and footers that should span all columns regardless of how many columns the grid has.
Shorthand: grid-area
The grid-area property is a shorthand that sets all four placement values at once: grid-row-start / grid-column-start / grid-row-end / grid-column-end.
.item {
grid-area: 1 / 2 / 3 / 4;
/* Row start: 1, Column start: 2, Row end: 3, Column end: 4 */
}
The order is row-start, column-start, row-end, column-end. It can be hard to remember, so many developers prefer the separate grid-column and grid-row properties for line-based placement, and reserve grid-area for named area references.
Named Grid Areas
Named grid areas are one of Grid's most readable features. The grid-template-areas property lets you create a visual map of your layout directly in CSS. Each string represents a row, and each word within the string represents a cell.
.page {
display: grid;
grid-template-areas:
"header header header"
"sidebar content content"
"footer footer footer";
grid-template-columns: 250px 1fr 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
gap: 1rem;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer { grid-area: footer; }
/* The layout looks exactly like the template:
┌──────────────────────────────────────┐
│ header │
├──────────┬───────────────────────────┤
│ │ │
│ sidebar │ content │
│ │ │
├──────────┴───────────────────────────┤
│ footer │
└──────────────────────────────────────┘ */
The beauty of this approach is that you can see the layout structure at a glance. Changing the layout for a different screen size is as simple as rewriting the template:
@media (max-width: 768px) {
.page {
grid-template-areas:
"header"
"content"
"sidebar"
"footer";
grid-template-columns: 1fr;
}
}
Notice how the sidebar moved below the content on mobile. You changed the entire layout without touching the HTML. The order of elements in the template controls the visual order, completely independent of the DOM order.
Empty Cells with the Dot Notation
Use a period (.) to represent an empty cell in the template:
.grid {
display: grid;
grid-template-areas:
"logo . nav"
". main ."
"footer footer footer";
grid-template-columns: 200px 1fr 200px;
}
/* ┌──────┬──────────┬──────┐
│ logo │ (empty) │ nav │
├──────┼──────────┼──────┤
│(emp) │ main │(emp) │
├──────┴──────────┴──────┤
│ footer │
└────────────────────────┘ */
Alignment: Controlling Item Position Within Cells
Grid provides a complete set of alignment properties that control where items sit inside their cells. These work on two axes: the inline axis (horizontal in left-to-right languages) and the block axis (vertical).
Container-Level Alignment
/* Align all items horizontally within their cells */
.grid {
justify-items: start; /* Left-align (default: stretch) */
justify-items: center; /* Center horizontally */
justify-items: end; /* Right-align */
justify-items: stretch; /* Fill the cell width (default) */
}
/* Align all items vertically within their cells */
.grid {
align-items: start; /* Top-align */
align-items: center; /* Center vertically */
align-items: end; /* Bottom-align */
align-items: stretch; /* Fill the cell height (default) */
}
/* Shorthand: align-items / justify-items */
.grid {
place-items: center; /* Center both axes */
place-items: start end; /* Top-align, right-align */
}
Item-Level Alignment
Override the container's alignment for individual items:
.special-item {
justify-self: center; /* Center this item horizontally */
align-self: end; /* Push this item to the bottom of its cell */
}
/* Shorthand */
.special-item {
place-self: end center; /* Bottom-center within its cell */
}
Aligning the Entire Grid Within Its Container
When the grid tracks do not fill the entire container (because you used fixed sizes, for example), you can align the entire grid within its container:
.grid {
display: grid;
grid-template-columns: repeat(3, 200px); /* 600px total */
width: 1000px; /* 400px of unused space */
/* Distribute tracks along the inline axis */
justify-content: center; /* Center the grid */
justify-content: space-between; /* Spread tracks with space between */
justify-content: space-evenly; /* Equal space around each track */
/* Distribute tracks along the block axis */
align-content: center;
align-content: space-between;
/* Shorthand */
place-content: center;
}
The distinction matters: justify-items and align-items control items within their cells. justify-content and align-content control the grid tracks within the container. These are different operations.
auto-fit and auto-fill: Responsive Grids Without Media Queries
The auto-fill and auto-fit keywords, used inside repeat(), are the foundation of responsive grid layouts that require zero media queries. They tell the browser to create as many columns as will fit within the container.
auto-fill
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
/* The browser calculates how many 250px columns fit in the
container width, creates that many columns, and distributes
remaining space equally (because of 1fr).
1200px container with 1rem gap:
4 columns at ~290px each
800px container:
3 columns at ~255px each
500px container:
1 column at 500px */
This is the single most useful CSS Grid pattern. It creates a fully responsive grid with one line of CSS. Cards, products, blog posts, image galleries — anything that should reflow based on available space.
auto-fit vs auto-fill
The difference between auto-fill and auto-fit only shows when there are fewer items than columns:
/* auto-fill: creates empty tracks to fill the space */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
/* With 2 items in a 1000px container:
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Item 1 │ │ Item 2 │ │(empty) │ │(empty) │
└────────┘ └────────┘ └────────┘ └────────┘
Items stay at 250px; empty columns fill remaining space */
/* auto-fit: collapses empty tracks to zero */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
/* With 2 items in a 1000px container:
┌────────────────────┐ ┌────────────────────┐
│ Item 1 │ │ Item 2 │
└────────────────────┘ └────────────────────┘
Items expand to fill all available space */
Use auto-fit when you want items to stretch and fill the entire row. Use auto-fill when you want items to maintain their size even when there is extra space. For most card grids and product listings, auto-fill with minmax() is the right choice.
Implicit vs Explicit Grid
The explicit grid is what you define with grid-template-columns and grid-template-rows. The implicit grid is what the browser creates automatically when items are placed outside the explicit grid, or when there are more items than cells.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3 explicit columns */
grid-template-rows: 200px 200px; /* 2 explicit rows = 6 cells */
/* But if you have 9 items, the browser creates a 3rd row.
That 3rd row is part of the implicit grid. */
/* Control implicit row size: */
grid-auto-rows: 200px;
/* Or let them grow with content but have a minimum: */
grid-auto-rows: minmax(150px, auto);
}
/* Control implicit columns (when items are placed beyond explicit columns) */
.grid {
grid-auto-columns: 200px;
}
/* Control the flow direction of auto-placed items */
.grid {
grid-auto-flow: row; /* Default: fill rows, create new rows as needed */
grid-auto-flow: column; /* Fill columns, create new columns as needed */
grid-auto-flow: dense; /* Backfill holes in the grid (can reorder items) */
}
The dense packing mode is worth highlighting. When items span different numbers of columns, the default auto-placement can leave gaps. grid-auto-flow: dense tells the browser to backfill those gaps with smaller items, even if it means rearranging the visual order. This is useful for image galleries and masonry-like layouts, but be aware it can create accessibility issues if visual order diverges too much from DOM order.
Grid vs Flexbox: When to Use Each
CSS Grid and Flexbox are complementary layout systems, not competitors. The general rule is straightforward: use Grid for two-dimensional layouts and Flexbox for one-dimensional layouts.
| Scenario | Use | Why |
|---|---|---|
| Navigation bar | Flexbox | One-dimensional row with variable-width items |
| Page layout (header/sidebar/content/footer) | Grid | Two-dimensional structure with named areas |
| Responsive card grid | Grid | Equal-width columns, no last-row stretch issue |
| Centering a single element | Either | Grid: place-items: center. Flexbox: justify-content + align-items |
| Button groups or toolbars | Flexbox | Inline items in a single row |
| Dashboard with mixed-size widgets | Grid | Items span multiple rows/columns |
| Card internal layout (title, body, footer) | Flexbox | Vertical column with margin-top: auto for footer |
| Image gallery with featured items | Grid | Spanning and overlapping capabilities |
| Form: label + input pairs | Grid | Two-column alignment with consistent widths |
The best production layouts use both. Grid for the outer page skeleton, Flexbox for the component-level layout inside grid cells. They nest naturally and each handles the problem it was designed for.
Responsive Grid Patterns Without Media Queries
One of Grid's greatest strengths is enabling responsive layouts with zero media queries. Here are the most practical patterns.
Pattern 1: The Flexible Card Grid
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
}
/* That is it. No media queries. The grid adapts:
- Wide screen (1200px): 3 columns
- Medium screen (900px): 2 columns
- Narrow screen (500px): 1 column */
Pattern 2: Sidebar That Collapses
.layout {
display: grid;
grid-template-columns: fit-content(250px) minmax(0, 1fr);
gap: 2rem;
}
/* fit-content(250px): sidebar shrinks to fit its content
but never exceeds 250px. On small screens where the
sidebar content is narrow, it takes only what it needs. */
Pattern 3: RAM (Repeat, Auto, Minmax)
/* The generalized responsive pattern: */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
gap: 1rem;
}
/* The min(100%, 250px) ensures that on very narrow screens
where 250px exceeds the container width, items shrink to
100% instead of causing horizontal overflow. This is the
bulletproof version of the auto-fill/minmax pattern. */
Pattern 4: Content-Width Center with Full-Bleed Sections
.page {
display: grid;
grid-template-columns:
1fr
min(65ch, 100% - 4rem)
1fr;
}
.page > * {
grid-column: 2; /* All content centered in the middle column */
}
.full-bleed {
grid-column: 1 / -1; /* Break out to full width */
width: 100%;
}
/* This pattern centers your content at a readable width (65ch)
while allowing specific elements like images or banners to
stretch edge-to-edge. No wrapper divs needed. */
Real-World Layout Examples
Here are three complete, production-ready layout examples that demonstrate Grid's power in real applications.
Example 1: The Holy Grail Layout
The holy grail is the classic layout: header across the top, three columns in the middle (navigation, content, sidebar), and a footer across the bottom. Developers struggled with this for decades using floats. Grid makes it trivial.
.holy-grail {
display: grid;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
gap: 1rem;
}
.header { grid-area: header; background: #16213e; padding: 1rem; }
.nav { grid-area: nav; background: #1a1a2e; padding: 1rem; }
.main { grid-area: main; background: #1a1a2e; padding: 2rem; }
.aside { grid-area: aside; background: #1a1a2e; padding: 1rem; }
.footer { grid-area: footer; background: #16213e; padding: 1rem; }
/* Stack on mobile */
@media (max-width: 768px) {
.holy-grail {
grid-template-areas:
"header"
"main"
"nav"
"aside"
"footer";
grid-template-columns: 1fr;
grid-template-rows: auto;
}
}
/* Desktop:
┌──────────────────────────────────┐
│ Header │
├────────┬───────────────┬─────────┤
│ │ │ │
│ Nav │ Content │ Aside │
│ │ │ │
├────────┴───────────────┴─────────┤
│ Footer │
└──────────────────────────────────┘
Mobile:
┌──────────────────┐
│ Header │
├──────────────────┤
│ Content │
├──────────────────┤
│ Nav │
├──────────────────┤
│ Aside │
├──────────────────┤
│ Footer │
└──────────────────┘ */
Example 2: Dashboard Layout with Mixed Widgets
.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: minmax(180px, auto);
gap: 1rem;
padding: 1rem;
}
/* Widget sizes */
.widget { background: #1a1a2e; border-radius: 8px; padding: 1.5rem; }
.widget-large { grid-column: span 2; grid-row: span 2; }
.widget-wide { grid-column: span 2; }
.widget-tall { grid-row: span 2; }
/* Responsive: fewer columns on smaller screens */
@media (max-width: 1024px) {
.dashboard {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 600px) {
.dashboard {
grid-template-columns: 1fr;
}
.widget-large, .widget-wide {
grid-column: span 1;
}
.widget-large, .widget-tall {
grid-row: span 1;
}
}
/* Desktop (4 columns):
┌────────────────────┬──────────┬──────────┐
│ │ Widget B │ │
│ Widget A (2x2) │ │ Widget D │
│ ├──────────┤ (tall) │
│ │ Widget C │ │
├──────────┬─────────┴──────────┴──────────┤
│ Widget E │ Widget F (wide) │
└──────────┴───────────────────────────────┘ */
Example 3: Responsive Card Grid with Subgrid
This is the most modern pattern, using subgrid to keep card content aligned across cards with varying content lengths.
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
}
.card {
display: grid;
grid-template-rows: subgrid; /* Inherit parent row tracks */
grid-row: span 4; /* Each card occupies 4 sub-rows */
gap: 0;
background: #1a1a2e;
border-radius: 8px;
overflow: hidden;
}
.card img {
width: 100%;
height: 200px;
object-fit: cover;
}
.card .title {
padding: 1rem 1rem 0.5rem;
font-size: 1.1rem;
font-weight: 600;
}
.card .description {
padding: 0 1rem;
color: #9ca3af;
font-size: 0.9rem;
}
.card .actions {
padding: 1rem;
margin-top: auto;
}
/* Without subgrid, titles misalign:
┌──────────────┐ ┌──────────────┐
│ [image] │ │ [image] │
│ Short Title │ │ A Much Longer│
│──────────────│ │ Title Here │
│ Description │ │──────────────│
│ [Button] │ │ Description │
└──────────────┘ └──────────────┘
With subgrid, everything aligns:
┌──────────────┐ ┌──────────────┐
│ [image] │ │ [image] │
│ Short Title │ │ A Much Longer│
│ │ │ Title Here │
│──────────────│ │──────────────│ ← aligned!
│ Description │ │ Description │
│ [Button] │ │ [Button] │
└──────────────┘ └──────────────┘ */
Subgrid is supported in all major browsers as of 2023. It solves the long-standing problem of aligning internal card elements across a row without resorting to fixed heights or JavaScript calculations.
Advanced Grid Techniques
Overlapping Grid Items
Unlike Flexbox, Grid items can occupy the same cells. This is useful for creating overlay effects, image captions, and decorative elements without absolute positioning.
.hero-section {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 400px;
}
.hero-section > * {
grid-column: 1;
grid-row: 1;
}
.hero-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.hero-overlay {
background: linear-gradient(transparent, rgba(0,0,0,0.8));
display: flex;
align-items: flex-end;
padding: 2rem;
z-index: 1;
}
Named Grid Lines
You can name grid lines to make placement more semantic:
.grid {
display: grid;
grid-template-columns:
[full-start] 1fr
[content-start] min(65ch, 100% - 4rem)
[content-end] 1fr
[full-end];
}
.article { grid-column: content-start / content-end; }
.banner { grid-column: full-start / full-end; }
Grid with Container Queries
Container queries pair beautifully with Grid, letting a component change its layout based on the container's width rather than the viewport:
.widget-container {
container-type: inline-size;
}
.widget {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@container (min-width: 500px) {
.widget {
grid-template-columns: 200px 1fr;
}
}
@container (min-width: 800px) {
.widget {
grid-template-columns: 200px 1fr 200px;
}
}
Masonry-Style Layout with Grid
While true CSS masonry is still in development as a specification, you can approximate it with Grid's dense packing and spanning:
.masonry {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
grid-auto-rows: 10px; /* Small row tracks */
grid-auto-flow: dense;
gap: 1rem;
}
/* Each item spans a different number of rows based on content */
.masonry .item-short { grid-row: span 20; } /* ~200px */
.masonry .item-medium { grid-row: span 30; } /* ~300px */
.masonry .item-tall { grid-row: span 40; } /* ~400px */
For a true masonry layout where items flow into the shortest column, the CSS masonry value for grid-template-rows is being standardized and is available behind a flag in Firefox. Until it ships everywhere, the approach above or a JavaScript masonry library is the practical solution.
Common Grid Properties Reference
Here is a concise reference of every major Grid property, grouped by whether it applies to the container or items.
Container Properties
/* Define the grid structure */
display: grid; /* or inline-grid */
grid-template-columns: ...; /* Track sizes for columns */
grid-template-rows: ...; /* Track sizes for rows */
grid-template-areas: ...; /* Named area map */
grid-template: rows / columns; /* Shorthand */
/* Gaps */
gap: 1rem; /* Both row and column gap */
row-gap: 1rem; /* Row gap only */
column-gap: 1rem; /* Column gap only */
/* Implicit grid behavior */
grid-auto-rows: minmax(100px, auto); /* Size of auto-created rows */
grid-auto-columns: 200px; /* Size of auto-created columns */
grid-auto-flow: row | column | dense;/* Auto-placement algorithm */
/* Item alignment within cells */
justify-items: start | end | center | stretch;
align-items: start | end | center | stretch;
place-items: <align> <justify>;
/* Grid alignment within container */
justify-content: start | end | center | space-between | space-around | space-evenly;
align-content: start | end | center | space-between | space-around | space-evenly;
place-content: <align> <justify>;
Item Properties
/* Placement */
grid-column: <start> / <end>; /* e.g., 1 / 3 or 1 / span 2 */
grid-row: <start> / <end>; /* e.g., 2 / 4 */
grid-area: <name>; /* Named area reference */
grid-area: <row-start> / <col-start> / <row-end> / <col-end>;
/* Self-alignment (overrides container's justify-items/align-items) */
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
place-self: <align> <justify>;
Browser Support
CSS Grid has been supported in all major browsers since 2017:
- Chrome — since version 57 (March 2017)
- Firefox — since version 52 (March 2017)
- Safari — since version 10.1 (March 2017)
- Edge — since version 16 (October 2017)
- Opera — since version 44 (March 2017)
As of 2026, CSS Grid has over 98% global browser support. You can use it without any fallbacks in virtually all projects. The newer features have slightly less support:
- Subgrid — supported in Chrome 117+, Firefox 71+, Safari 16+, Edge 117+. Available in all current browser versions.
- Masonry layout — still in specification development. Available in Firefox behind a flag. Not yet in Chrome, Safari, or Edge stable builds.
- Container queries — supported in Chrome 105+, Firefox 110+, Safari 16+. Universally available.
For the core Grid specification (everything covered in this guide except masonry), you can use it with full confidence. There is no need for @supports checks or fallback layouts in any current project.
Performance Notes
Grid layout is fast. Browsers have optimized their Grid implementations significantly since 2017. However, there are a few things worth keeping in mind for very complex layouts:
auto-fillwithminmax()requires the browser to calculate how many columns fit on every resize. For simple grids, this is imperceptible. For grids with hundreds of items, consider debouncing resize handlers or usingcontent-visibility: autofor off-screen items.- Subgrid adds calculation overhead because child items must align with parent tracks. The cost is minimal for typical card grids but could be measurable in deeply nested subgrids.
- Avoid unnecessary nesting. A flat grid with well-placed items outperforms a grid-within-grid-within-grid structure. Use named areas and spanning instead of nesting when possible.
grid-auto-flow: denserequires the browser to check all previous cells for gaps, which scales with the number of items. For very large grids (500+ items), dense packing is measurably slower than the default row flow.
For the vast majority of real-world layouts (cards, dashboards, page structures, forms), Grid performance is not a concern. Optimize for readability and maintainability first.
Summary and Quick Reference
CSS Grid is the definitive tool for two-dimensional layout in CSS. Here is a quick decision framework for using it effectively:
- Start with
display: gridon your container. - Define columns with
grid-template-columns. Usefrfor flexible columns,px/remfor fixed columns, andminmax()for bounded flexibility. - Use
repeat(auto-fill, minmax(min-size, 1fr))for responsive grids that need no media queries. - Use named
grid-template-areasfor page-level layouts where readability matters. - Place items with
grid-columnandgrid-rowwhen you need explicit control, or let auto-placement handle simple grids. - Use
gapfor spacing. Never use margins between grid items. - Combine Grid + Flexbox: Grid for the outer structure, Flexbox for components inside grid cells.
- Use
subgridwhen child elements need to align with parent grid tracks (card grids with titles, descriptions, and buttons that should align across cards).
The best way to internalize CSS Grid is to build real layouts with it. Start with the responsive card grid pattern (one line of CSS) and work your way up to named areas and dashboard layouts. Within a few projects, Grid will become your default choice for any layout that involves rows and columns.
For a quick-reference table of every Grid property with one-line descriptions and values, keep our CSS Grid Cheat Sheet bookmarked. To experiment with grid layouts visually before writing code, use the CSS Grid Generator. And if you want to compare Grid with Flexbox in detail, read our CSS Flexbox vs Grid guide.