Skip to content

Flexbox Fundamentals

beginner11 min read

One-Dimensional Layout, Solved

Flexbox handles layout in one direction -- either a row or a column. It distributes space among items, aligns them, and handles wrapping. Centering something? Spacing items evenly? Building a navbar? Flexbox is your tool.

But here's the key insight most developers miss: flexbox isn't controlled by individual items. It's controlled by the container. The container decides the direction, wrapping, and alignment. Items just participate by declaring how much they can grow, shrink, or what their base size is.

Mental Model

Think of flexbox as a conveyor belt in a factory. The belt moves in one direction (the main axis). Items are placed on the belt and slide along it. justify-content controls the spacing between items on the belt. align-items controls how items are positioned vertically relative to the belt (the cross axis). flex-grow lets certain items stretch to fill empty belt space. flex-shrink lets items compress when the belt is too short.

The Two Axes

Every flex container has two axes:

  • Main axis: The direction items flow. Set by flex-direction.
  • Cross axis: Perpendicular to the main axis.
.row { flex-direction: row; }        /* Main: horizontal, Cross: vertical */
.row-reverse { flex-direction: row-reverse; }
.column { flex-direction: column; }  /* Main: vertical, Cross: horizontal */
.column-reverse { flex-direction: column-reverse; }

This matters because justify-content always works on the main axis and align-items always works on the cross axis. When you switch flex-direction from row to column, justify and align swap their visual effect.

Container Properties

.container {
  display: flex;

  /* Direction */
  flex-direction: row;

  /* Wrapping */
  flex-wrap: nowrap;    /* Default: all items on one line */
  flex-wrap: wrap;      /* Items wrap to next line if needed */

  /* Main axis alignment */
  justify-content: flex-start;    /* Pack to start (default) */
  justify-content: flex-end;      /* Pack to end */
  justify-content: center;        /* Center items */
  justify-content: space-between; /* Equal space between, no edge space */
  justify-content: space-around;  /* Equal space around each item */
  justify-content: space-evenly;  /* Equal space everywhere */

  /* Cross axis alignment (single line) */
  align-items: stretch;      /* Default: items stretch to fill container */
  align-items: flex-start;   /* Align to cross start */
  align-items: flex-end;     /* Align to cross end */
  align-items: center;       /* Center on cross axis */
  align-items: baseline;     /* Align text baselines */

  /* Cross axis alignment (multiple lines with wrap) */
  align-content: flex-start;
  align-content: center;
  align-content: space-between;

  /* Gap between items */
  gap: 1rem;          /* Both row and column gap */
  row-gap: 1rem;      /* Gap between wrapped rows */
  column-gap: 0.5rem; /* Gap between columns */
}

Item Properties

flex-grow

This is where the fun starts. Controls how much an item grows relative to siblings when there's extra space:

/* Three items, container is 900px wide, each item is 200px base */
/* Extra space: 900 - 600 = 300px */

.item-a { flex-grow: 1; } /* Gets 1/6 of 300px = 50px → total 250px */
.item-b { flex-grow: 2; } /* Gets 2/6 of 300px = 100px → total 300px */
.item-c { flex-grow: 3; } /* Gets 3/6 of 300px = 150px → total 350px */

flex-shrink

Controls how much an item shrinks when there isn't enough space:

/* Three items of 400px each in a 900px container */
/* Overflow: 1200 - 900 = 300px needs to be absorbed */

.item-a { flex-shrink: 1; } /* Shrinks 1/3 of overflow = 100px → 300px */
.item-b { flex-shrink: 1; } /* Shrinks 1/3 of overflow = 100px → 300px */
.item-c { flex-shrink: 1; } /* Shrinks 1/3 of overflow = 100px → 300px */
Common Trap

flex-shrink is weighted by the item's flex-basis (or width). An item with flex-basis: 600px and flex-shrink: 1 gives up more pixels than an item with flex-basis: 200px and flex-shrink: 1, even though both have shrink value 1. The formula is: shrink amount = (flex-shrink × flex-basis) / sum(all flex-shrink × flex-basis) × total overflow.

flex-basis

The initial size of an item before growing or shrinking:

.item {
  flex-basis: 200px;  /* Start at 200px, then grow/shrink */
  flex-basis: 30%;    /* 30% of the container */
  flex-basis: auto;   /* Use the item's width/height (default) */
  flex-basis: 0;      /* Start at 0 — flex-grow fully controls size */
  flex-basis: content; /* Size to content intrinsic size */
}

The flex Shorthand

In practice, you'll almost always use this instead of the individual properties.

.item {
  flex: 1;        /* flex-grow: 1, flex-shrink: 1, flex-basis: 0% */
  flex: 0 1 auto; /* Default: don't grow, can shrink, size to content */
  flex: 1 1 0%;   /* Grow and shrink equally, from zero base */
  flex: none;     /* flex: 0 0 auto — rigid, no grow/shrink */
  flex: auto;     /* flex: 1 1 auto — grow/shrink from content size */
}
Info

flex: 1 and flex: 1 1 auto are NOT the same. flex: 1 sets flex-basis: 0%, meaning all items start at zero and grow into available space equally. flex: 1 1 auto starts from each item's content size and distributes remaining space. The difference is visible when items have different content lengths.

align-self

Override the container's align-items for a specific item:

.container { align-items: flex-start; }
.special-item { align-self: flex-end; } /* This item aligns to end */

order

Change visual order without changing DOM order:

.sidebar { order: 2; }  /* Visually second */
.main { order: 1; }     /* Visually first */
/* Default order is 0. Lower values appear first. */
Execution Trace
Container
display: flex, flex-direction: row, gap: 1rem
Main axis = horizontal
flex-basis
Each item starts at its flex-basis size
auto = content size, 0 = nothing
Available space
Container width minus sum of flex-basis minus gaps
Positive = room to grow, negative = need to shrink
Distribute
Extra space distributed by flex-grow ratios
Or items shrink by flex-shrink × flex-basis ratio
Align
justify-content on main, align-items on cross
Applied after sizing is complete

Production Scenario: Equal-Width Items Regardless of Content

This comes up all the time with tabs and nav items.

/* Problem: Items have different content lengths */
.tabs {
  display: flex;
}
.tab {
  flex: 1 1 auto; /* Items grow from content size — unequal widths */
}

/* Fix: Start from zero base so content doesn't affect width */
.tab {
  flex: 1 1 0%; /* All tabs equal width regardless of content */
  text-align: center;
}
What developers doWhat they should do
Confusing justify-content and align-items when flex-direction changes
The axes swap when flex-direction changes. justify and align don't swap.
justify-content always affects the MAIN axis. When direction is column, justify is vertical.
Using flex: 1 and flex: 1 1 auto interchangeably
The flex-basis value determines the starting point before grow/shrink distribution.
flex: 1 sets basis to 0% (equal sizes). flex: 1 1 auto keeps content-based sizing.
Setting width on flex items instead of flex-basis
flex-basis has priority over width in flex layout. Using both creates confusion.
Use flex-basis or the flex shorthand. Width works but flex-basis is the correct way.
Adding flex: 1 to every item when only one should grow
flex: 1 on all items means equal sizing. Often you want one flexible area and fixed-size sidebars.
Only add flex-grow to items that should absorb extra space
Quiz
A flex container (row) has 3 items. Item A has flex: 2, Item B has flex: 1, Item C has flex: 1. The container is 800px with no gaps. How wide is Item A?
Quiz
What does align-items: baseline do in a flex container?
Quiz
Three items each have flex: 0 1 400px in a 900px container. What happens?
Key Rules
  1. 1justify-content works on the main axis, align-items on the cross axis — this doesn't change when axes swap
  2. 2flex: 1 (basis: 0%) makes items equal size. flex: 1 1 auto distributes space based on content size.
  3. 3flex-shrink is weighted by flex-basis — larger items give up more pixels
  4. 4Use gap instead of margin for spacing between flex items
  5. 5The flex shorthand (flex: grow shrink basis) is preferred over individual properties
1/10