CSS Grid Layout -- A Practical Guide with Examples

Learn CSS Grid from scratch with practical examples for common web layouts like dashboards, galleries, and holy grail.

Introduction to CSS Grid

CSS Grid has fundamentally changed how we build web layouts. Unlike Flexbox, which excels at one-dimensional layouts, Grid gives you complete control over both rows and columns. It's the most powerful layout system available in modern CSS, and once you understand the core concepts, you'll find it incredibly intuitive.

In this guide, we'll walk through everything you need to know to build professional, responsive layouts with Grid -- from basic container setup to advanced techniques that eliminate the need for media queries in many situations.

Understanding Grid Containers and Items

The first step is understanding the relationship between containers and items. When you apply display: grid to an element, it becomes a grid container, and its direct children become grid items.

.grid-container {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  gap: 20px;
}

This simple declaration creates a layout with 3 columns: a fixed 200px sidebar on the left, a flexible middle column that grows with available space (the 1fr unit), and another 200px column on the right. All items automatically get 20px of spacing between them.

Grid Template Columns and Rows

grid-template-columns is where the magic happens. It defines how many columns your grid has and how much space each should take. You can mix units freely: pixels, percentages, the flexible fr unit (fraction), and auto.

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

Here, repeat(4, 1fr) creates 4 equal columns. The repeat() function eliminates repetitive code -- imagine writing 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr for 8 columns instead.

For rows, use grid-template-rows similarly:

.app-layout {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: 60px 1fr 50px;
  height: 100vh;
}

This creates a classic layout: a 250px sidebar, a 60px header, a flexible main content area, and a 50px footer.

Understanding the FR Unit

The fr unit represents a fraction of available space after fixed items are accounted for. It's the secret to building responsive layouts without media queries.

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

In this example, the first column gets 250px. The remaining space is divided into 3 parts (2fr + 1fr = 3fr total). The middle column gets 2/3 of the remaining space, and the right column gets 1/3.

This means if your viewport is 1000px wide and the first column takes 250px, you have 750px left. The middle column gets 500px (2/3 of 750px) and the right column gets 250px (1/3 of 750px). Resize your window, and these proportions automatically adjust.

Gap and Spacing

The gap property controls spacing between grid items. It applies both horizontally and vertically.

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

You can also use separate properties for more control:

.layout {
  gap: 20px 30px; /* row gap: 20px, column gap: 30px */
}

Gap is cleaner than margin-based approaches because it doesn't double-up spacing on edges or between items. A 20px gap between 3 items creates exactly 40px of total spacing, not 60px.

Named Grid Areas for Complex Layouts

For layouts with multiple content regions, named grid areas make your code incredibly readable.

.holy-grail {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: 60px 1fr 50px;
  grid-template-areas:
    "header header header"
    "sidebar content aside"
    "footer footer footer";
  gap: 16px;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }

This approach is powerful for media queries. You can completely reorganize your layout by changing the grid-template-areas while keeping your HTML unchanged:

@media (max-width: 768px) {
  .holy-grail {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto auto auto;
    grid-template-areas:
      "header"
      "sidebar"
      "content"
      "aside"
      "footer";
  }
}

Responsive Layouts with MINMAX and AUTO-FIT

The minmax() function and auto-fit or auto-fill keywords create truly responsive grids without media queries.

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

This creates a grid where each column is at least 250px wide. As the screen shrinks, columns wrap to new rows. As it expands, more columns appear -- all automatically. At 1200px you get 4 columns, at 800px you get 3, at 600px you get 2.

auto-fit collapses empty grid tracks (creating fewer, wider columns), while auto-fill keeps empty tracks around (useful in specific scenarios).

grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
/* Shrinks: 6 columns -> 3 columns -> 1 column */

grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
/* Grows: keeps adding columns even if some are empty */

Building Common Layout Patterns

Card Grid (E-Commerce, Portfolio)

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

.card {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: hidden;
}

This creates a responsive product grid. On mobile it's 1 column, on tablet 2-3 columns, on desktop 4+ columns. No media queries needed.

Sidebar Layout

.sidebar-layout {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 32px;
  padding: 24px;
}

@media (max-width: 768px) {
  .sidebar-layout {
    grid-template-columns: 1fr;
  }
}

The sidebar is always 280px on desktop, and the main content grows to fill available space. On mobile, the sidebar stacks above the content.

Dashboard Layout

.dashboard {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  grid-auto-rows: minmax(200px, auto);
  gap: 16px;
}

.card-large {
  grid-column: span 2;
}

Using grid-auto-rows: minmax(200px, auto) ensures items maintain a minimum height while expanding for content. The span 2 makes certain cards wider for featured content.

Advanced Grid Techniques

Implicit Grid with Auto Placement

Grid automatically creates rows or columns when items don't fit. Control this with grid-auto-rows and grid-auto-columns:

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

All rows automatically get 100px height, even ones you didn't explicitly define.

Grid Line Names

For complex layouts, you can name grid lines:

.layout {
  display: grid;
  grid-template-columns:
    [start] 200px [sidebar-end] 1fr [content-end] 200px [end];
}

.featured {
  grid-column: sidebar-end / content-end;
}

This is more readable than counting columns.

Justify and Align Content

Control item placement within their cells:

.grid-container {
  display: grid;
  grid-template-columns: repeat(3, 150px);
  justify-items: center; /* horizontal */
  align-items: center;   /* vertical */
}

Or align the entire grid within its container:

justify-content: center;
align-content: space-around;

Performance and Browser Support

CSS Grid is supported in all modern browsers (95%+ coverage as of 2026). IE11 had partial support but is no longer relevant for new projects.

Grid performs excellently -- it's one of the fastest layout methods available. Render and paint times are minimal compared to floats or flexbox for complex layouts.

Common Grid Mistakes to Avoid

Mistake 1: Applying grid to too many elements. Grid is for major layout structure, not micro-layouts. Use Flexbox for smaller components.

Mistake 2: Hardcoding all column widths with pixels. Embrace fr units and auto for flexibility.

Mistake 3: Overcomplicating with unnecessary explicit grid areas. Simple layouts don't need them.

Mistake 4: Forgetting about content. Always test your grid with real, varying content lengths.

Conclusion

CSS Grid is powerful, flexible, and surprisingly simple once you understand the core concepts. The fr unit, minmax() function, and auto-fit keyword give you the tools to build responsive layouts without media queries in most cases. Named grid areas make complex multi-region layouts readable and maintainable.

Start with basic grids using grid-template-columns, add gap for spacing, and progressively explore named areas and advanced techniques as your layouts become more complex. Your layouts will be cleaner, faster, and more maintainable than anything you could achieve with older methods.

Related Tools