Half of CSS layout problems I help juniors with come down to one mistake: they reached for flexbox when they wanted grid, or vice versa, and then spent an hour fighting the wrong tool. The fight always ends with position: absolute overrides, hardcoded widths in media queries, and a layout that breaks the moment a designer adds another item. The fix is shorter than the fight: pick the right tool, write five lines of CSS, move on.
The rule that has saved me the most time: flexbox is for layouts in one dimension (a row, or a column), and grid is for layouts in two dimensions (rows AND columns at the same time). That sentence is in every modern CSS resource and it is still the best heuristic I have. Once I started asking "is this layout one-dimensional or two-dimensional" before writing any CSS, the choice between flexbox and grid stopped being a decision and started being a consequence.
The one-dimensional vs two-dimensional rule
Look at the layout you want to build and ask: do I care about how items align in one direction (a single row, or a single column), or in both directions at once?
The wrap case is the one people stumble on. "Toolbar that wraps onto multiple rows" might sound two-dimensional, but it is not: the wrap is reactive, not designed. You are not specifying "these go on row 1, those go on row 2"; you are saying "flow them in a row, and let them wrap when they run out of space". That is one-dimensional with overflow. Flexbox handles it.
A gallery where item 5 spans two columns and item 7 takes a full row is two-dimensional: you are placing items in a coordinate system. Grid handles that.
When flexbox is right
Flex container, flex items, one main axis, one cross axis. The properties you reach for most:
The shapes flexbox is unbeatable at:
- Header navigation. Logo on the left, nav links in the middle, user avatar on the right.
justify-content: space-betweenand you are done. - Centering anything. A modal that should sit in the center of its container:
display: flex; align-items: center; justify-content: centeron the parent. Two lines, works at every viewport size. - Toolbar with overflow. A row of buttons that should wrap onto a second row when there is not enough space:
display: flex; flex-wrap: wrap; gap: 0.5remon the parent. - Sidebar that stacks. A vertical stack of items with consistent spacing:
display: flex; flex-direction: column; gap: 1rem.
The flex shorthand is the part people misread most often. flex: 1 means flex: 1 1 0, which means "grow to fill available space, shrink if needed, treat the basis as 0". flex: 0 1 auto (the default) means "do not grow, shrink if needed, basis is the item's content size". flex: 1 0 200px means "grow, do not shrink, and start at a basis of 200px so each item gets at least that". The mental model: flex is (grow, shrink, basis) and the basis is the starting size before grow/shrink kicks in.
A subtle point that catches people: flex items default to flex-shrink: 1, which means they will shrink below their content size if the container is too small. A row of three items with long text can squish each item below the readable width. The fix is flex-shrink: 0 on items that should never shrink, or min-width: 0 to allow text overflow handling instead of squishing the box.
When grid is right
Grid is the choice the moment you have content placed in both rows and columns. The properties:
The shapes grid wins:
- App shell layouts. Header, sidebar, main, footer all positioned in the page at once. Trying to do this with nested flexboxes is a textbook source of frustration; grid does it in one container.
- Form layouts. A two-column form where labels sit in column 1 and inputs in column 2, with rows for each field.
grid-template-columns: max-content 1frlines up labels by their natural width and lets inputs grow. - Photo or card galleries with intrinsic rows and columns.
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr))produces a responsive gallery that adds and removes columns as the viewport resizes, without media queries. This trick alone has saved me hundreds of lines of breakpoint CSS. - Layouts where items overlap. Grid items can occupy the same cells. A hero with a heading layered over a background image is one grid container with two children placed in the same cell.
The property I underused for years: minmax(min, max). It accepts a minimum and a maximum size for a track. minmax(0, 1fr) is the trick that prevents grid columns from blowing out when content is wider than the column allows; the default 1fr is actually minmax(auto, 1fr), where auto means "as wide as the content", and a long unbreakable string can push the column wider than its share. minmax(0, 1fr) clamps the minimum to zero and lets the column behave like a real fraction.
They compose: grid for the page, flex for the row
The layouts I write in production are rarely pure grid or pure flex. They are usually grid for the page-level structure and flex for the local arrangements inside individual sections.
Three levels of layout, two tools, no fights with either. Grid handles the two-dimensional page shape; flex handles the one-dimensional rows and columns inside it. Trying to use grid for the header (one row of items) is overkill; trying to use flex for the page (two dimensions) is fighting the tool.
Default to grid for the page, flex for the row
The rule I follow now, and the one I would tell anyone starting a new layout: use grid for the page-level shape, use flexbox inside any section that is one-dimensional. Reach for the other only when the layout actually needs both dimensions or only one. If you find yourself with position: absolute overrides, hardcoded margins to fake spacing, or a layout that breaks at one specific viewport width, you probably picked the wrong tool, and the rewrite to the right tool is usually shorter than the workaround you were about to ship.
The two specs (Flexbox Level 1 and Grid Level 1) are both fully supported in every browser anyone targets in 2026. There is no compatibility excuse left. The remaining work is just picking the right one for the layout you have, and that is a five-second question, not a five-hour problem.
