Learn CSS · Lesson 6 of 7 · 9 min

Flexbox

Flexbox is the tool you reach for when you want a handful of things lined up neatly in one direction: a row of buttons, a navbar, a centered card. You tell a container to be flexible, and its children fall into line and share the space. Once it clicks, half of everyday layout stops being a fight.

The container and its items

Flexbox is always a relationship between one parent and its direct children. The moment you write display: flex on an element, that element becomes a flex container and every direct child becomes a flex item. By default the items sit in a single horizontal row, each only as wide as it needs to be, all aligned along the top.

.toolbar {
  display: flex;     /* this element is now a flex container */
}
/* every direct child of .toolbar is now a flex item,
   laid out in a row, side by side */

That is the whole on-switch. The properties you set on the container (such as justify-content and align-items, below) control how the group is arranged; properties you set on an item (such as flex-grow) control how that one item behaves inside the group. Keeping that container-versus-item split straight is most of the battle.

Two axes: main and cross

Here is the idea that makes everything else make sense. A flex container has two axes. The main axis is the direction the items flow in; the cross axis runs at a right angle to it. With the default flex-direction: row, the main axis runs left to right and the cross axis runs top to bottom.

Flip the direction with flex-direction: column and the items stack vertically instead. The main axis is now vertical and the cross axis is horizontal. Nothing else about flexbox changes; the same properties keep working, they just point a new way.

flex-direction: row

main axis →
123
cross ↓

flex-direction: column

main axis ↓
123
cross →
The main axis follows flex-direction; the cross axis is always at a right angle to it.

Why this matters: justify-content always aligns along the main axis, and align-items always aligns along the cross axis. Learn which axis is which and you will never have to guess which property to reach for again.

justify-content: spacing along the main axis

justify-content decides how items are positioned and spaced along the main axis. In a normal row, that means horizontally. The values you will use constantly are flex-start (the default, items packed to the start), center (packed in the middle), space-between (first and last item pinned to the edges, equal gaps in between), and space-around (equal space around every item). Watch the same three items move:

justify-content: flex-start

123

justify-content: center

123

justify-content: space-between

123

justify-content: space-around

123
The same three items, four values of justify-content, on the main (horizontal) axis.

The difference between space-between and space-around trips people up: space-between puts no space at the outer edges (items hug the ends), while space-around gives every item an equal cushion, so there is a half-cushion of space at each edge. space-between is the classic choice for a header with a logo on the left and a menu on the right.

align-items: lining up on the cross axis

align-items aligns items along the cross axis, which in a normal row is the vertical direction. The default is stretch, which makes every item grow to the full height of the container, handy for equal-height cards. Switch it to center and items of different heights line up on a shared center line; flex-start pins them to the top, flex-end to the bottom. Notice the items below are deliberately different heights so the alignment is visible:

align-items: stretch

123

align-items: center

123

align-items: flex-start

123

align-items: flex-end

123
Items of different heights, aligned four ways on the cross (vertical) axis.

A quick aside on a near-twin: align-items aligns items on the cross axis, while align-content (which only kicks in once items wrap onto multiple lines) spaces those whole lines. For a single row of items, align-items is the one you want.

gap: the clean way to space items

For years people put margins on flex items to separate them, then fought with the unwanted margin on the first or last child. Forget all that. gap sets even spacing between flex items in one declaration on the container, with no leftover space at the ends and nothing to clean up.

.toolbar {
  display: flex;
  gap: 12px;          /* 12px between every item, full stop */
}

gap: 0

1234

gap: 20px

1234
One property on the container, even spacing between every item.

Unlike vertical margins, gap never collapses (see the box model lesson for why margins do). It is the modern default for spacing, and it works identically in grid, so it is worth making a habit.

Flexible sizing and wrapping

The "flex" in flexbox is about letting items grow and shrink to fill the available space. Three properties on each item control this:

  • flex-grow (default 0): how greedily an item expands to soak up leftover space. An item with flex-grow: 1 takes all the slack; two items both at 1 split it evenly.
  • flex-shrink (default 1): how willingly an item gives up size when the container is too small. Set it to 0 to stop an item from shrinking below its natural size.
  • flex-basis (default auto): the item's starting size along the main axis, before any growing or shrinking happens.

You will almost always write them together with the flex shorthand, in the order grow shrink basis. The one you will type most is flex: 1, which means "grow to share space equally," perfect for columns that should split a row:

.sidebar { flex: 0 0 240px; }  /* fixed: no grow, no shrink, 240px */
.content { flex: 1; }          /* flexible: take all remaining space */

.sidebar { flex: 0 0 120px }  ·  .content { flex: 1 }

sidebarcontent (flex: 1)
A fixed-width item beside a flexible one that absorbs the rest of the row.

By default a flex container keeps all items on one line, squashing them if it must. Add flex-wrap: wrap and items that no longer fit drop onto a new line instead of overflowing, the foundation of responsive rows that reflow on small screens:

.cards {
  display: flex;
  flex-wrap: wrap;     /* items spill onto new lines as needed */
  gap: 16px;
}

The two patterns you will use forever

Two recipes cover an enormous share of real layout work. Commit them to memory.

1. Perfect centering. Centering a thing both horizontally and vertically used to be a legendary CSS headache. With flexbox it is three lines: turn the parent into a flex container, center on the main axis, center on the cross axis.

.hero {
  display: flex;
  justify-content: center;   /* center on the main axis  */
  align-items: center;       /* center on the cross axis */
  min-height: 300px;
}

justify-content: center + align-items: center

perfectly centered
The classic dead-center box, both axes at once.

2. The navbar. A logo pinned to the left, links pinned to the right, vertically aligned in the middle: that is justify-content: space-between plus align-items: center. This single pattern is on nearly every site you visit.

.nav {
  display: flex;
  justify-content: space-between;  /* logo left, links right */
  align-items: center;            /* everything on one line  */
}

justify-content: space-between + align-items: center

The everyday navbar: brand on the left, links on the right.

Practice

Two minutes each, and each one locks in a single idea. Use a blank CodePen, your browser's DevTools, or one of our tools to experiment:

  1. Center a single div in the dead center of its parent both ways, using display: flex with justify-content: center and align-items: center. Then resize the parent and confirm it stays centered.
  2. Build a navbar: a logo on the left and three links on the right, vertically centered. Start from display: flex, then add justify-content: space-between and align-items: center.
  3. Make a two-column layout: a fixed 200px sidebar with flex: 0 0 200px next to a content area with flex: 1. Confirm the content stretches to fill whatever space is left.

The mental model to keep

  • display: flex turns a parent into a container and its direct children into items.
  • The main axis follows flex-direction; the cross axis is always at a right angle to it.
  • justify-content aligns on the main axis; align-items aligns on the cross axis.
  • Use gap to space items, flex: 1 to share space, and flex-wrap to reflow.

That is flexbox: one-dimensional layout that finally behaves. When you need rows and columns at once, full two-dimensional control, that is the job of CSS Grid, the next lesson. Flexbox handles a line; grid handles a plane.