The problem: Flexbox is not a grid, and people kept using it as one
Before CSS Grid shipped in 2017, front-end developers built two-dimensional layouts with Flexbox, floats, and inline-block. Flexbox is one-dimensional — it lays out items along a single axis (row or column) and wraps them when it runs out of room. That is fine for navbars, button groups, and card rows. It is wrong for full-page layouts, where you have rows and columns that need to align to each other across the entire page. People forced it with percentage widths, hard-coded heights, and nth-child margins. The hacks worked until the viewport changed.
CSS Grid is the two-dimensional system. You define rows and columns on a container, and the children drop into the cells where the tracks intersect. The container owns the layout, not the children. You can align tracks to each other, span a child across multiple tracks, and name regions of the grid with strings. The model is older than Flexbox — Microsoft shipped an early version for Windows 8 tiles — but the spec landed in all major browsers in March 2017. There is no reason to write a page layout with Flexbox in 2026.
The CSS Grid Generator writes the declarations from a visual editor. You set the columns, rows, gaps, alignment, and named areas, and it produces the CSS, the Tailwind classes, and the matching HTML. It does not handle responsive breakpoints, subgrid, or auto-fit. It is a layout sketcher, not a production system. Knowing what it can and cannot do is the difference between using it well and shipping a layout that breaks on mobile.
Fastest path
Open the CSS Grid Generator. Pick a preset (Holy Grail, Card Grid, Dashboard, Magazine, Gallery) or start from the default 3-column, 2-row grid. Add or remove columns and rows. For each track, set a value and a unit (fr, px, %, auto, minmax). Set the row and column gap with the sliders. Pick justify-items, align-items, justify-content, and align-content from the dropdowns. To name a region, type a name in the box and drag across the preview cells. Switch between CSS, Tailwind, and HTML output tabs. Copy the code, or download the CSS file.
The units: fr, px, %, auto, minmax
The tracks in grid-template-columns and grid-template-rows are sized with units, and the unit you pick is the decision that matters most. Five units cover most layouts.
fr (fraction) distributes free space proportionally. Three 1fr columns split the container width into three equal parts. A 2fr 1fr split gives the first column two-thirds of the space. The fr unit only divides the space left over after fixed-size tracks (px, percent with a fixed parent) take their share. A 250px 1fr 1fr declaration gives the first column 250 pixels and splits the rest evenly.
px fixes a track to a pixel width regardless of the container. Useful for sidebars, headers, and fixed-width content. The downside is that a px track does not respond to the viewport — a 250px sidebar eats 250 pixels on a 320-pixel phone, leaving 70 for the content. Pair px tracks with media queries or minmax to make them responsive.
% sizes a track relative to the container's inline size. A 33% 33% 33% split produces three equal columns — but percent tracks do not account for the gap, so three 33% columns plus a gap overflow the container. Use fr instead of percent for equal splits.
auto sizes a track to its content. An auto column is as wide as its widest child. This is useful when you want a column to shrink to its content (a button column, an icon column) but dangerous when the content is wider than the leftover space — the auto track grows past the container and the grid overflows. Use minmax(0, auto) if you need auto behavior with an overflow guard.
minmax(min, max) sets a floor and a ceiling on a track. minmax(200px, 1fr) collapses to 200 pixels on a narrow container and grows to 1fr on a wide one. This is the closest thing to a responsive track without writing a media query — the track is fluid between its bounds. The generator exposes min and max inputs for columns when you pick minmax as the unit. The rows do not expose min and max inputs — a row set to minmax silently uses the defaults of minmax(100px, 1fr), which is almost never what you want for a row.
The combination that handles most layouts is a fixed unit (px) for one or two tracks and fr for the rest. A 250px sidebar with a 1fr content column is the standard app shell. A 200px header with 1fr body rows is the standard document layout. Mixing minmax into the fr tracks gives you a responsive floor without a media query.
Named areas beat numbered lines for real layouts
CSS Grid lets you place children two ways: by line numbers (grid-column: 1 / 3) or by named areas (grid-area: header). The line-number approach works for small grids and falls apart on real layouts. A 12-column dashboard with 8 placed children means 16 numbers to track, and a refactor that moves one region means renumbering every line that referenced it.
Named areas solve this. You declare the layout as strings on the container:
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
Each string is a row. Each word is a cell. Repeating a word across cells spans that region. A period is an empty cell. You read the layout from the declaration. You refactor by editing the strings. You place a child with grid-area: header and the browser figures out the lines.
The generator's drag-to-name interface produces the grid-template-areas string and the matching grid-area declarations from a visual selection. The five presets (Holy Grail, Dashboard, Magazine, and the two simpler ones) are each a named-area layout you can inspect and modify. Named areas have one constraint the generator does not enforce: the regions must be rectangular. An L-shaped area is invalid CSS and the browser drops the whole declaration. The generator does not warn you if you drag an L — it produces the string, and the browser silently ignores it. Keep your regions rectangular.
Alignment: justify-items vs justify-content
Four alignment properties, and the difference between them is the most common source of grid confusion.
justify-items and align-items align children inside their grid cells. justify-items: center centers each child horizontally within its cell. align-items: center centers each child vertically. The default is stretch, which makes the child fill the cell. This is why grid children fill their cells by default and Flexbox children do not.
justify-content and align-content align the grid tracks inside the container. If your grid tracks take less space than the container (say, three 200px columns in a 1000px container), justify-content: space-between pushes the tracks apart with the leftover space between them. If your tracks fill the container, justify-content does nothing.
The names are confusing because "justify" and "align" mean different things in Flexbox. In Flexbox, justify is along the main axis and align is along the cross axis. In Grid, justify is always inline (horizontal) and align is always block (vertical), regardless of the grid's orientation. If you learned Flexbox first, this is the one place the mental model does not transfer.
The generator exposes all four. The Tailwind output only emits justify-items and items (align-items) — it drops justify-content and align-content. If you need content-level alignment in Tailwind, you have to add it manually. The CSS output includes all four.
Gap, and why it replaced margin hacks
The gap property is the space between grid tracks. gap: 8px sets both row and column gap to 8 pixels. gap: 8px 16px sets the row gap to 8 and the column gap to 16. Before gap shipped, you added spacing with margins on children and negative margins on the container to pull them back. Every nth-child selector to remove the last margin was a bug waiting to happen. Gap replaces all of it with one declaration on the container.
The generator caps gap at 64 pixels. That is enough for most layouts. If you need more, edit the output.
What the generator does not do
A visual grid generator is a layout sketcher, not a production system. Five things it does not handle, all of which you will need for real layouts.
No responsive breakpoints. The generator produces a single static set of declarations. There are no media queries. A 3-column layout that works on desktop collapses on mobile to a 1-column layout you have to write yourself. The minmax unit gives you fluid tracks without a media query, but a full responsive redesign (changing the number of columns, reordering areas, collapsing the sidebar) needs a @media (max-width: 768px) block you write by hand.
No auto-fit or auto-fill. The repeat(auto-fit, minmax(200px, 1fr)) pattern is how you build a card grid that reflows from 4 columns on desktop to 1 column on mobile with one declaration and no media queries. The generator does not produce repeat() and does not support auto-fit or auto-fill. Every column is enumerated explicitly. For a reflowing card grid, write the declaration by hand.
No subgrid. Subgrid lets a child grid inherit the parent's tracks, so nested grids line up with the outer grid. It shipped in 2023 and is supported in all major browsers. The generator does not support it. For nested layouts that need to align across levels, write the subgrid declaration by hand.
No repeat(). grid-template-columns: repeat(12, 1fr) is shorthand for twelve 1fr tracks. The generator enumerates each track explicitly, which is fine for small grids and unreadable for large ones. The 12-column utility grid that most CSS frameworks use is one repeat(12, 1fr) declaration; the generator would emit twelve 1fr values.
No framework-specific output. The generator produces CSS, Tailwind classes, and HTML. It does not produce React, Vue, Svelte, or SCSS. The HTML output is a flat list of divs — you have to map it to your component structure yourself.
Gotchas
- minmax on rows uses hidden defaults. The generator exposes min and max inputs for columns but not for rows. A row set to minmax silently emits
minmax(100px, 1fr). If you need a custom min or max on a row, edit the output by hand. Typing 0 in the max box for a column also falls back to1fr, not0px— the tool treats 0 as a sentinel. - Named areas must be rectangular. The generator does not validate the shape of a dragged area. An L-shaped region produces an invalid
grid-template-areasstring, and the browser silently drops the entire declaration. Keep your areas rectangular, or the layout reverts to the default flow. - The Tailwind output drops justify-content and align-content. It emits justify-items and items (align-items) but not the content-level alignment properties. If you need content-level alignment in Tailwind, add it manually with
justify-content-{value}andcontent-{value}utilities. - Tailwind's grid-rows-N caps at 6, grid-cols-N caps at 12. The generator uses the native Tailwind classes for 1-6 rows and 1-12 columns. Beyond those, it falls back to arbitrary value syntax (
grid-rows-[1fr_1fr_1fr_1fr_1fr_1fr_1fr]). The fallback works, but it is not documented in the UI. A 7-row uniform grid silently switches syntax. - Percent tracks do not account for gap. Three 33% columns plus a gap overflow the container. Use fr for equal splits; percent is a trap for anything other than a single track.
- auto tracks can overflow. An auto column grows to fit its content. If the content is wider than the container's leftover space, the grid overflows. Use
minmax(0, auto)when you need auto behavior with an overflow guard. - No responsive breakpoints are generated. The output is one static layout. Any responsive behavior — collapsing columns on mobile, reordering areas, changing the number of tracks — needs media queries you write by hand. The minmax unit gives you fluid tracks without a media query, but it does not change the structure of the grid.
- Download only works for CSS. The download button is hard-coded to the CSS output. If you are on the Tailwind or HTML tab, the download still gives you the CSS file. Copy the other formats from the active tab.
- The HTML output is a flat div list. It does not include your component structure. You have to map the generated divs to your React or Vue components yourself.
Summary
- CSS Grid is the two-dimensional layout system. Flexbox is one-dimensional. Page layouts that need aligned rows and columns belong in Grid; component-level layouts that lay out along a single axis belong in Flexbox. The container owns the Grid layout, not the children.
- The units are the decision that matters. fr distributes free space proportionally. px fixes a track. % is a trap for equal splits (it does not account for gap). auto sizes to content and can overflow. minmax(min, max) sets a floor and ceiling, and is the closest thing to a responsive track without a media query. Mixing a fixed unit for one track and fr for the rest covers most layouts.
- Named grid areas beat numbered lines for real layouts.
grid-template-areaswith string rows lets you read the layout from the declaration, refactor by editing the strings, and place children withgrid-area: name. The regions must be rectangular — an L-shaped area is invalid CSS and the browser drops the whole declaration. The generator does not validate the shape. - justify-items and align-items align children inside their cells. justify-content and align-content align the grid tracks inside the container. The names do not map to Flexbox's names — in Grid, justify is always horizontal and align is always vertical. The gap property replaces margin hacks with one declaration on the container.
- The CSS Grid Generator writes the template-columns, template-rows, gap, alignment, and named-area declarations from a visual editor. It does not handle responsive breakpoints, subgrid, auto-fit/auto-fill, or the repeat() function. It is a layout sketcher, not a production system. The minmax UI for rows uses hidden defaults. The Tailwind output drops justify-content and align-content and caps native grid-rows-N at 6. Use the CSS Minifier on the output, the JSON Schema Generator for the API responses the layout serves, and the Regex Tester for the validation patterns on the form fields.