Community Article

Flexbox vs Grid: Pick the Right Tool

Flexbox is one-dimensional layout, Grid is two-dimensional. Most layout battles end faster when you pick the right one before you start writing CSS.

Flexbox vs Grid: Pick the Right Tool

Flexbox is one-dimensional layout, Grid is two-dimensional. Most layout battles end faster when you pick the right one before you start writing CSS.

css-flexbox
css-grid
frontend
css-responsive-design
arjunpatel

By @arjunpatel

November 27, 2025

·

Updated May 18, 2026

868 views

4

4.3 (12)

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?

Layout shape -> tool
  Nav bar with three items, evenly spaced              -> flex (one row)
  Sidebar that stacks items vertically                  -> flex (one column)
  A 12-column page layout with header / sidebar / main  -> grid (two dimensions)
  A photo gallery with rows AND columns of images       -> grid (two dimensions)
  A toolbar of icons that wrap onto multiple rows       -> flex with flex-wrap
  A form with labels in column 1 and inputs in column 2 -> grid

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:

.container {
    display: flex;
    flex-direction: row;        /* or column */
    justify-content: space-between;  /* main axis distribution */
    align-items: center;         /* cross axis alignment */
    gap: 1rem;
}

.item {
    flex: 0 1 auto;  /* grow, shrink, basis */
}

The shapes flexbox is unbeatable at:

  1. Header navigation. Logo on the left, nav links in the middle, user avatar on the right. justify-content: space-between and you are done.
  2. Centering anything. A modal that should sit in the center of its container: display: flex; align-items: center; justify-content: center on the parent. Two lines, works at every viewport size.
  3. 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.5rem on the parent.
  4. 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:

.container {
    display: grid;
    grid-template-columns: 250px 1fr;   /* sidebar fixed, main fills */
    grid-template-rows: auto 1fr auto;  /* header, content, footer */
    grid-template-areas:
        "header  header"
        "sidebar main"
        "footer  footer";
    gap: 1rem;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }

The shapes grid wins:

  1. 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.
  2. 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 1fr lines up labels by their natural width and lets inputs grow.
  3. 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.
  4. 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.

/* Page level: grid */
.page {
    display: grid;
    grid-template-columns: 240px 1fr;
    grid-template-rows: 64px 1fr;
    grid-template-areas:
        "header header"
        "sidebar main";
}

/* Header content: flex */
.header {
    grid-area: header;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 1.5rem;
}

/* Sidebar items: flex column */
.sidebar {
    grid-area: sidebar;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

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.

Back to Articles