What flexbox is for

Flexbox (the CSS Flexible Box Layout module) arranges elements along a single line: a row of menu links, a toolbar of buttons, a label next to an input, a column of cards. It works in one dimension at a time and lets the items share the available space: they can grow, shrink, be spaced out or be aligned, all without knowing their sizes in advance.

Before flexbox, these layouts were built with floats, inline-block and margin tricks, and centering something vertically was a running joke. Flexbox has worked in every major browser for years and is the everyday tool for component layout. For two-dimensional page layouts with rows and columns, use CSS Grid.

The flex container and its items

Setting display: flex on an element makes it a flex container. Its direct children become flex items and are laid out by flexbox; grandchildren are not affected (they follow normal flow inside their parent, unless that parent is a flex container too). The container itself still behaves like a block on the outside. Use display: inline-flex for a container that sits inside a line of text.

The menu of the Kestrel Energy portal is a list, which tells screen readers how many links there are. One declaration turns the vertical list into a row:

<nav aria-label="Portal">
  <ul class="menu">
    <li><a href="#">Dashboard</a></li>
    <li><a href="#">Meter readings</a></li>
    <li><a href="#">Invoices</a></li>
    <li><a href="#">Contact</a></li>
  </ul>
</nav>
.menu {
  display: flex;      /* the ul is the container, each li an item */
  gap: 1.5rem;
  list-style: none;
  padding: 0;
}

Flex items are "blockified": a span or a that is a flex item accepts width, height and vertical margins, as a block would. And margins between flex items never collapse.

The main axis and the cross axis

Every flex container has two axes. The main axis is the direction in which the items are placed one after another, set by flex-direction. The cross axis runs at right angles to it. Almost every flexbox property works along one of the two, so knowing which is which is the key to the whole module.

The two axes of a flex container. With flex-direction row, items are placed left to right along the horizontal main axis and the cross axis runs top to bottom. With flex-direction column, items are placed top to bottom along the vertical main axis and the cross axis runs left to right. flex-direction: row 1 2 3 main axis cross axis flex-direction: column 1 2 3 main axis cross axis
flex-direction turns the axes: in a row the main axis is horizontal, in a column it is vertical. (In a right-to-left language a row runs from right to left.)

flex-direction has four values: row (the default, in the text direction), column (top to bottom), and row-reverse and column-reverse, which flip the start and end.

<ol class="row">
  <li>Read meter</li>
  <li>Enter number</li>
  <li>Confirm</li>
</ol>
<ol class="column">
  <li>Read meter</li>
  <li>Enter number</li>
  <li>Confirm</li>
</ol>
ol { display: flex; gap: 0.5rem; list-style: none; padding: 0; margin: 0 0 1rem; }
.column { flex-direction: column; }
li { border: 1px solid steelblue; background: aliceblue; padding: 0.5rem; }

Notice that in the column the items are as wide as the container. That is the default align-items: stretch at work on the cross axis, explained below.

Distributing items: justify-content

justify-content decides what happens to the free space on the main axis, the space left over when the items do not fill the line:

  • flex-start (the default): items packed at the start.
  • flex-end: packed at the end.
  • center: packed in the middle.
  • space-between: first and last item against the edges, equal space between the others.
  • space-around: equal space around each item, so the edges get half as much as the gaps between items.
  • space-evenly: exactly equal spaces, including at both edges.
<p class="label">flex-start</p>
<div class="bar start"><span>Day</span><span>Night</span><span>Total</span></div>
<p class="label">center</p>
<div class="bar center"><span>Day</span><span>Night</span><span>Total</span></div>
<p class="label">space-between</p>
<div class="bar between"><span>Day</span><span>Night</span><span>Total</span></div>
<p class="label">space-evenly</p>
<div class="bar evenly"><span>Day</span><span>Night</span><span>Total</span></div>
.label { margin: 0; font-family: monospace; }
.bar { display: flex; border: 1px dashed gray; margin: 0.25rem 0 1rem; }
.bar span { background: lightsteelblue; padding: 0.25rem 0.75rem; }
.start   { justify-content: flex-start; }
.center  { justify-content: center; }
.between { justify-content: space-between; }
.evenly  { justify-content: space-evenly; }

A common pattern is a header with the logo on the left and the menu on the right: justify-content: space-between on the header, with the two as its items. Another is margin-left: auto on one item, which pushes it and everything after it to the end, for example a "Log out" link at the far right of a menu.

Aligning items: align-items

align-items aligns the items on the cross axis, inside their line:

  • stretch (the default): items stretch to fill the line's height (in a row) or width (in a column), unless they have a fixed size. This is why flex cards in a row are equally tall.
  • flex-start, flex-end, center: aligned to the start, the end or the middle of the cross axis.
  • baseline: aligned so the first lines of text sit on the same baseline, useful when items have different font sizes.

A single item can override the container's setting with align-self. In the banner below the icon is much larger than the text; align-items: center lines them up through the middle:

<div class="banner">
  <span class="icon" aria-hidden="true">⚡</span>
  <p>Planned maintenance on Sunday, 02:00 to 04:00.</p>
  <a href="#">Details</a>
</div>
.banner {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 1rem;
  border: 1px solid steelblue;
  background: aliceblue;
}
.banner p { margin: 0; }
.icon { font-size: 2rem; }

Centering in both directions

With both properties set to center, an item sits in the exact middle of its container, whatever the sizes:

<div class="stage">
  <p class="message">No outages reported in your area.</p>
</div>
.stage {
  display: flex;
  justify-content: center;  /* main axis: horizontal */
  align-items: center;      /* cross axis: vertical */
  min-height: 8rem;
  border: 1px dashed gray;
}
.message { margin: 0; }

The container needs a height larger than its content for vertical centering to show; here min-height provides it.

Space between items: gap

The gap property sets the space between items in a flex or grid container, not at the outer edges. gap: 1rem sets both directions; row-gap and column-gap set them separately (they matter when items wrap onto several lines). Gap in flexbox has been Baseline widely available since October 2023.

Before gap worked in flexbox, the space came from margins on the items, followed by an extra rule to remove the margin of the last item, and negative margins on the container when items wrapped. gap replaces all of that: it only ever appears between items, and it never collapses or doubles.

Wrapping onto several lines

By default all items stay on one line (flex-wrap: nowrap) and are squeezed when there is not enough room, down to the width of their longest word, and then they overflow. flex-wrap: wrap lets items move to a new line instead. Each line is then laid out as its own little flex row.

Combined with a minimum size per item, that gives a set of cards that rearranges itself for the available width, without media queries. The frame below is as wide as this page's text column: make your browser window narrower and the cards wrap onto more lines.

<div class="plans">
  <article class="plan">
    <h2>Fixed</h2>
    <p>The same price per kWh for three years.</p>
  </article>
  <article class="plan">
    <h2>Variable</h2>
    <p>The price follows the market and is updated every month. No fixed term.</p>
  </article>
  <article class="plan">
    <h2>Dynamic</h2>
    <p>A new price every hour.</p>
  </article>
</div>
.plans {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
.plan {
  flex: 1 1 12rem;   /* grow, shrink, start from 12rem */
  border: 1px solid gray;
  padding: 0 1rem;
}
.plan h2 { font-size: 1.125rem; }

All three cards are equally tall in each line, because of align-items: stretch, even though their text has a different length. When the items wrap onto several lines, align-content distributes the lines on the cross axis, the way justify-content distributes items on the main axis.

flex-grow, flex-shrink and flex-basis

Three properties on the items decide how big each one is along the main axis. The browser starts from each item's basis, adds up the bases, and compares the total with the container: if there is space left, the items grow; if there is too little, they shrink.

flex-basis: the starting size

flex-basis is an item's starting size on the main axis, before any free space is shared out or taken away. The default, auto, means "use my width (or height in a column) if set, otherwise the size of my content". A length such as 12rem or 0 overrides that.

flex-grow: sharing out free space

flex-grow is a number that says how large a share of the free space an item receives. The default is 0: the item does not grow. Free space is divided in proportion to the numbers. In the example below the container is 320px wide and each item starts at 40px, so 320 − 3 × 40 = 200px is free. The shares are 1 : 1 : 2, so the items get 50, 50 and 100px extra: they end up 90, 90 and 140px wide.

<div class="track">
  <div class="a">grow 1</div>
  <div class="b">grow 1</div>
  <div class="c">grow 2</div>
</div>
.track { display: flex; width: 320px; outline: 1px dashed gray; }
.track div {
  box-sizing: border-box;
  flex-basis: 40px;
  border: 1px solid steelblue;
  background: aliceblue;
  padding: 0.5rem 0;
  text-align: center;
}
.a, .b { flex-grow: 1; }
.c     { flex-grow: 2; }

Note that flex-grow: 2 does not make an item twice as wide as one with flex-grow: 1; it gets twice as much of the free space. Only when every basis is 0 are the final widths in the same ratio as the grow numbers.

A real use: a search form where the input takes all the space the label and button leave over.

<form class="search" role="search">
  <label for="q">Search help</label>
  <input id="q" type="search" name="q">
  <button type="submit">Search</button>
</form>
.search { display: flex; align-items: center; gap: 0.5rem; }
.search input { flex-grow: 1; min-width: 0; }
.search input, .search button { font: inherit; padding: 0.25rem 0.5rem; }

flex-shrink: giving back space

flex-shrink works the other way round: when the items do not fit, it says how much each one gives up. The default is 1, so all items shrink, larger ones more. Set flex-shrink: 0 on an item that must keep its size, such as an icon or an avatar.

Items will not shrink below their minimum content size, usually the width of their longest word, because the default min-width of a flex item is auto. A long URL or a child with white-space: nowrap can therefore push a flex item out of its container. The fix is min-width: 0 on the item (as on the search input above), which allows it to shrink fully.

The flex shorthand

In practice the three properties are set together with flex: grow shrink basis. A few values cover almost every case:

Shorthand Means Use for
flex: initial 0 1 auto (the default) Content-sized items that may shrink
flex: 1 1 1 0% Items that share all the space equally
flex: auto 1 1 auto Items that grow, starting from their content size
flex: none 0 0 auto Items that keep their size: no growing, no shrinking
flex: 1 1 12rem Grow and shrink from 12rem Wrapping cards, as in the plans example

Visual order and source order

The order property moves an item along the main axis (lower numbers first; the default is 0), and row-reverse or column-reverse flip the whole line. Both change only what is drawn. The keyboard focus order and the order in which screen readers read the content still follow the HTML. Press Tab in the result below: after "2. Enter reading" the focus jumps back to "3. Confirm" at the far left.

<nav class="steps" aria-label="Steps">
  <a href="#">1. Log in</a>
  <a href="#">2. Enter reading</a>
  <a href="#" class="moved">3. Confirm</a>
</nav>
.steps { display: flex; gap: 1rem; }
.moved { order: -1; }   /* drawn first, but still focused last */

When the visual order and the source order disagree, keyboard and screen reader users get a different page from sighted mouse users. WCAG success criteria 1.3.2 (Meaningful Sequence) and 2.4.3 (Focus Order) exist for exactly this. Put the HTML in the order that makes sense and use order only for small visual tweaks that do not change the meaning. (A reading-flow property that makes focus follow the visual order is being developed, but as of 2026 only Chromium-based browsers support it.)

Flexbox or grid?

Both are layout modes for a container's children, and they share gap, justify-content and align-items. The difference is the number of dimensions:

  • Flexbox lays out in one direction and lets the content decide the sizes. Items in one line know nothing about items in the next line, so wrapped cards do not line up in columns. Good for menus, toolbars, button groups, a label next to a field, and centering.
  • CSS Grid defines rows and columns together, and items line up in both directions. Good for page layouts, card grids with straight columns, and forms with aligned labels.

They combine well: a grid for the page, flexbox inside the header and the cards. See also Responsive Design for changing the layout at different screen sizes.