Flexbox Fundamentals
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.
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 */
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 */
}
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. */
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 do | What 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 |
- 1justify-content works on the main axis, align-items on the cross axis — this doesn't change when axes swap
- 2flex: 1 (basis: 0%) makes items equal size. flex: 1 1 auto distributes space based on content size.
- 3flex-shrink is weighted by flex-basis — larger items give up more pixels
- 4Use gap instead of margin for spacing between flex items
- 5The flex shorthand (flex: grow shrink basis) is preferred over individual properties