Tools · CSS · Flexbox playground · Guides · Why Flex Items Never End Up the Width You Gave Them
Why Flex Items Never End Up the Width You Gave Them
You give a flex item width: 200px, and it renders at 173px. Or you set three items to flex-grow: 1 expecting three equal columns, and the one with the longest label comes out wider. Neither is a browser bug. Flexbox treats every size you give it as a starting point on one line, then runs a second step that shares out the space that is left over, or takes back the space that is missing. Most flexbox confusion is that second step happening without you noticing.
One axis, and a size that is only a starting point
A flex container lays its children out along a single line called the main axis. With flex-direction: row that line runs in the writing direction; with column it runs top to bottom. Each item gets a starting size on that axis from flex-basis. When the basis is auto, the browser falls back to the item's width (or height in a column), and if that is also unset, to the size of its content.
Then the browser adds the bases up, adds the gaps, and compares the total with the container. The difference is the free space. If it is positive, items with a flex-grow above zero take a share of it in proportion to their grow values. If it is negative, items with a flex-shrink above zero give some back. The defaults are grow 0 and shrink 1, which is why an untouched row sits at the start and leaves a gap at the end, but squeezes when the container gets narrow.
Why "grow 1 on everything" is not equal columns
Take a 600px container with no gap and three items whose content makes them 100px, 100px and 200px wide. Give all three flex-grow: 1. The free space is 600 minus 400, so 200px, split three ways: about 67px each. The result is roughly 167, 167 and 267. Equal shares of the leftover, unequal items.
The fix is to make the starting sizes equal before the split. flex: 1 is shorthand for flex-grow: 1; flex-shrink: 1; flex-basis: 0%. With a basis of zero, all 600px is free space, and three equal grow values give three 200px items no matter what is inside them. The difference between flex-grow: 1 and flex: 1 is only the basis, and the basis is the whole difference.
Why an item refuses to shrink
Shrinking has two quirks. First, it is weighted by the basis, not just the shrink value: a 300px item with shrink 1 gives back three times as much as a 100px item with shrink 1. That keeps small items from collapsing to nothing first.
Second, flex items have min-width: auto by default, which in flexbox means "no narrower than my content". A long URL, a wide table or a pre block sets that floor, and the item stops shrinking there, pushing the row wider than its container. The usual fix is min-width: 0 on the item that holds the long content, so shrinking can actually happen and the content inside can scroll or wrap instead.
The quickest way to stop guessing is to watch the split happen. Flexbox playground draws a real flex container: change one item's grow, shrink or basis and the other items move to make room, with the matching CSS written underneath. This guide uses it for the most common job, one fixed item and one that fills the rest, and this one follows a real header row through what happens when its words get longer.