
CSS Grid Generator Guide — Build Two-Dimensional Layouts with Confidence
📷 Christina Morillo / PexelsCSS Grid Generator Guide — Build Two-Dimensional Layouts with Confidence
Master CSS Grid with practical examples. Learn grid-template-columns, fr units, repeat(), auto-fill, auto-fit, and minmax() — then build a real photo gallery and dashboard layout from scratch.
Why Grid Took Me So Long to Actually Learn
I avoided CSS Grid for longer than I should have. Flexbox handled most of what I needed, and every time I poked at Grid documentation I'd hit terminology like "implicit tracks" and "named grid lines" and quietly close the tab. It felt like overkill.
The thing that changed my mind was trying to build a dashboard layout with Flexbox. I had a sidebar, a header, a main content area, and a footer, and I was nesting flex containers three levels deep just to get things to line up. It worked — barely — but any time the design changed I had to restructure the HTML. That's when someone pointed me toward Grid, and within about an hour I rewrote the whole thing in 30 lines of CSS. The HTML barely changed.
That's the thing Grid does that Flexbox can't: it lets you define layout in two dimensions at once, from the container, without caring about item nesting. You define the structure, and the items just slot into it.
This guide covers everything you actually need to use Grid day-to-day — the core properties, the weird-but-useful functions, and the patterns I've shipped in real projects. If you want to experiment visually as you read, the CSS Grid Generator on ToolBox Hubs lets you configure columns, rows, and gaps and see the generated CSS in real time — it's a good companion to working through these concepts.
The Mental Model
Before any code, here's the mental model: CSS Grid asks you to think about layout the way a graphic designer thinks about a layout grid. You define a series of horizontal and vertical lines, and those lines create cells. Then you place content into those cells, or let the browser auto-place it for you.
This is genuinely different from Flexbox, where layout is driven by the items themselves — you set properties on the items that tell them how to grow, shrink, or align. Grid inverts this. Layout is defined on the container, and items follow.
Defining a Grid
It starts with one property:
.container {
display: grid;
}
That doesn't do much on its own. Items will stack in a single column by default. The interesting part is defining tracks.
grid-template-columns and grid-template-rows
These define your column and row sizes. The values are a space-separated list of track sizes.
.container {
display: grid;
grid-template-columns: 200px 400px 200px;
grid-template-rows: 100px auto 60px;
}
This creates three columns (200px, 400px, 200px) and three rows (100px, whatever height the content needs, 60px). Items are placed into cells from left to right, top to bottom.
You can use any CSS length unit: px, em, rem, %, vw. But the most useful unit Grid introduced is brand new.
The fr Unit
The fr unit stands for fraction. It represents a share of the available free space in the grid container. Here's why it's so much better than percentages:
/* Percentage approach — you have to do the math */
.container {
display: grid;
grid-template-columns: 25% 50% 25%;
}
/* fr approach — you just describe the ratios */
.container {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
Both create the same layout, but fr handles gaps correctly. If you add a gap between columns and you're using percentages, the total width exceeds 100% and you get overflow. With fr, the browser calculates the free space after gaps are subtracted, then distributes it. Much less math.
.container {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 24px;
/* The browser subtracts the two 24px gaps first,
then splits the remainder in 1:2:1 ratio */
}
You can also mix fixed widths with fr units, which is how most real layouts work:
.layout {
display: grid;
grid-template-columns: 280px 1fr;
/* sidebar is always 280px, content takes everything else */
}
That pattern — fixed sidebar, flexible content — is one I use constantly.
The repeat() Function
When you have many equal-width columns, writing them out by hand gets tedious. repeat() is the shorthand:
/* Without repeat */
.grid {
grid-template-columns: 1fr 1fr 1fr 1fr;
}
/* With repeat */
.grid {
grid-template-columns: repeat(4, 1fr);
}
repeat(4, 1fr) means "four tracks, each 1fr wide." You can also repeat complex patterns:
.grid {
grid-template-columns: repeat(3, 100px 1fr);
/* Creates: 100px 1fr 100px 1fr 100px 1fr */
}
But the really powerful version combines repeat() with two special keywords.
auto-fill and auto-fit
This is where Grid goes from "useful" to "kind of magical." Instead of specifying an exact column count, you can tell the browser to create as many columns as will fit:
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, 200px);
gap: 16px;
}
This creates a grid that automatically adjusts the number of columns based on the container width. On a wide screen you might get 5 columns, on a narrow screen you get 2. No media queries needed.
The difference between auto-fill and auto-fit is subtle but meaningful:
auto-fill creates as many tracks as will fit, including empty ones. If you only have 3 items but the grid could fit 6 columns, you get 6 columns — the last 3 are empty but occupy space.
auto-fit also creates as many tracks as will fit, but collapses empty tracks to zero width. If you have 3 items in a grid that could fit 6, the empty tracks collapse and your 3 items stretch to fill the available space.
/* auto-fill: empty columns preserve space */
.fill-grid {
grid-template-columns: repeat(auto-fill, 200px);
}
/* auto-fit: items expand to fill the container */
.fit-grid {
grid-template-columns: repeat(auto-fit, 200px);
}
Which one to use depends on the design. For a photo gallery where you want items to stay a consistent size, auto-fill is usually better. For a card grid where you want items to fill the row, auto-fit is the right call.
minmax()
The last piece of the puzzle is minmax(), which defines a size range for a track rather than a fixed size. It takes two arguments: a minimum size and a maximum size.
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(200px, 1fr));
}
This creates three columns that are at least 200px wide and at most 1fr of the available space. On a wide screen they expand. On a narrow screen they never get smaller than 200px.
The combination of repeat(), auto-fill or auto-fit, and minmax() is so common it has become the canonical responsive grid pattern:
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 24px;
}
Read this as: "create as many columns as will fit, each at least 240px wide, each growing equally to fill available space." Items wrap automatically. No media queries. No JavaScript. This is the pattern I reach for when I need a responsive card grid in a hurry.
Real-World Example: Photo Gallery
Let me build a photo gallery from scratch to show how this plays out.
<div class="gallery">
<img src="photo-1.jpg" alt="...">
<img src="photo-2.jpg" alt="...">
<!-- more photos -->
</div>
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 8px;
}
.gallery img {
width: 100%;
aspect-ratio: 1 / 1;
object-fit: cover;
display: block;
}
That's it. The grid automatically figures out how many columns fit in the container. Images maintain a square aspect ratio using aspect-ratio and object-fit: cover handles cropping. On mobile you might get 2 columns, on a wide desktop you might get 6. The layout is responsive by nature, not by a list of breakpoints.
If you want to feature certain photos, you can span them across multiple cells:
.gallery img:first-child {
grid-column: span 2;
grid-row: span 2;
}
That makes the first photo take up a 2x2 block while everything else flows around it. No position tricks, no floats — just Grid placement.
Real-World Example: Dashboard Layout
This is the use case that sold me on Grid. A dashboard with a sidebar, header, main content, and footer.
<div class="dashboard">
<header class="dash-header">Header</header>
<nav class="dash-sidebar">Sidebar</nav>
<main class="dash-main">Main Content</main>
<footer class="dash-footer">Footer</footer>
</div>
.dashboard {
display: grid;
grid-template-columns: 260px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
'header header'
'sidebar main'
'footer footer';
min-height: 100vh;
gap: 0;
}
.dash-header { grid-area: header; }
.dash-sidebar { grid-area: sidebar; }
.dash-main { grid-area: main; }
.dash-footer { grid-area: footer; }
The grid-template-areas property is the thing that makes this particularly readable. You're drawing the layout in ASCII art, basically. The header and footer span both columns. The sidebar is 260px. The main content fills everything else.
Making this responsive is straightforward:
@media (max-width: 768px) {
.dashboard {
grid-template-columns: 1fr;
grid-template-rows: 60px auto 1fr 40px;
grid-template-areas:
'header'
'sidebar'
'main'
'footer';
}
}
On mobile, everything stacks in a single column. The sidebar collapses below the header. Clean.
Grid vs Flexbox: The Real Answer
People treat this as if you have to pick a side. You don't. They solve different problems.
Use Grid when:
- You need rows and columns to align with each other
- You're building page-level structure (header, sidebar, main, footer)
- You need items to span multiple rows or columns
- You want to define layout from the container, not the items
- You're building a consistent grid of cards with aligned content areas inside each card
Use Flexbox when:
- You have a single row or column of items
- Items need to size themselves based on their content
- You're arranging navigation links, buttons, or form inputs inline
- You need items to wrap but don't care about cross-row alignment
In most projects I use both. Grid handles the page skeleton. Flexbox handles the component internals — the nav bar, the button group inside a card, the form layout. They're complementary, not competing.
The clearest rule I've heard: "Grid is for layout. Flexbox is for alignment." It's a simplification, but it'll steer you right most of the time.
Using the CSS Grid Generator
If you're building a grid layout and you want to see the result before committing it to code, the CSS Grid Generator on ToolBox Hubs is worth opening. You can set the number of columns and rows, adjust the gap, configure column and row sizes visually, and get the exact CSS output to paste into your project.
It's particularly useful when you're working with grid-template-areas — visualizing which areas span which cells is much easier with a visual tool than staring at code. You configure the layout once, copy the CSS, and drop it in. Faster than writing it by hand, and a good way to quickly test a layout idea before building it out.
Browser Support and Real Caveats
Grid has strong browser support — every modern browser has had full support since 2017. You don't need vendor prefixes. You don't need a polyfill. Write it, ship it.
The one genuine caveat: Internet Explorer 11. IE 11 has an older Grid implementation (based on an early draft spec) using -ms- prefixed properties that are mostly incompatible with modern Grid syntax. If your analytics show IE 11 traffic you care about, Grid usage needs careful consideration or a fallback.
For everyone not supporting IE 11 — which, honestly, is most of us now — Grid is production-ready without caveats.
One property to be careful about: subgrid. It lets child grids inherit and participate in the parent grid's track definitions, which is useful for aligning nested content. Safari added support in 2023, Chrome in late 2023. As of early 2026 support is solid across the board, but if you're building on projects with older browsers in scope, verify your specific targets.
The Properties You'll Actually Use
After working with Grid on multiple projects, these are the properties I reach for most:
/* The core definition */
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
grid-template-rows: auto;
/* Named areas for complex layouts */
grid-template-areas: 'header' 'sidebar main' 'footer';
/* Placing items */
grid-column: span 2;
grid-row: span 2;
/* Or explicit placement */
grid-column: 1 / 3; /* starts at line 1, ends at line 3 */
grid-row: 2 / 4;
/* Spacing */
gap: 24px;
column-gap: 24px;
row-gap: 16px;
The implicit grid properties (grid-auto-rows, grid-auto-columns) are worth knowing too. When items get auto-placed into rows that weren't explicitly defined, grid-auto-rows sets their height:
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 200px; /* all auto-placed rows are 200px tall */
}
And if you want auto-placed items to fill columns rather than rows, grid-auto-flow: column flips the placement direction.
What Clicked for Me
The moment Grid stopped feeling intimidating was when I stopped trying to memorize every property and just internalized three things:
- You define columns and rows on the container, not the items.
- The
frunit distributes free space proportionally. repeat(auto-fill, minmax(min, 1fr))is the pattern for responsive grids.
Everything else — named areas, spanning, explicit placement — you can look up as needed. But those three concepts get you through 80% of real-world Grid usage.
CSS Grid is one of those tools that feels like it's doing the work for you once you understand it. A layout that would have taken 50 lines of floats or deeply nested flexbox in 2015 now takes 10 lines of Grid. That's a real quality-of-life improvement, and after a while you'll struggle to remember what layout felt like before it.