Topic 7.3
flex-grow, flex-shrink and flex-basis
In one line
Each flex item starts from its flex-basis; leftover space is shared out in proportion to flex-grow, and missing space is taken back in proportion to flex-shrink times the basis, never below the item's minimum size, which by default is its content width, hence the min-width: 0 fix.
Think of it like this
Sharing a pizza. Everyone first gets their starting slice (that's flex-basis). If pizza is left over, it's handed out by how hungry each person is: someone with a hunger of 2 gets twice as much as someone with 1 (that's flex-grow). If there wasn't enough pizza to start with, slices are trimmed back, and people with bigger slices give up more (that's flex-shrink). But nobody's slice can be cut smaller than the topping in the middle (the item's minimum size).
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- `flex-basis`
- An item's starting size on the main axis, before growing or shrinking.
automeans use its width, or its content size. - `flex-grow`
- How big a share of the leftover space an item takes. Default
0: it doesn't grow. - `flex-shrink`
- How much an item gives up when there isn't enough space, weighted by its basis. Default
1. - `flex` shorthand
- One property for all three:
flex: grow shrink basis, likeflex: 1 1 0%. - Positive free space
- Space left over after every item has its basis. Shared out by
flex-grow. - Negative free space
- How much the bases overflow the container. Taken back by
flex-shrink. - Min-content size
- The smallest an element can be without its content overflowing: the width of its longest word or widest unbreakable child.
- Automatic minimum size
- Flex items' default
min-width: auto, which stops them shrinking below their min-content size. - Frozen item
- An item that hit its min or max size during sharing. It stays there while the rest is shared again.
Step by step
01Find each item's basis
Three items in a 240px row each have flex-basis: 40px. Together they need 120px, so 120px is free. Nothing happens to it yet: the default flex-grow is 0.
.row { display: flex; width: 240px; }
.row > * { flex-basis: 40px; } /* 3 x 40 = 120px used */02Share the free space with flex-grow
Give the items flex-grow values 1, 1 and 2. The total is 4, so each "share" is 120 ÷ 4 = 30px. The items get 30, 30 and 60px extra: final widths 70, 70 and 100px.
.a, .b { flex-grow: 1; } /* 40 + 30 = 70px */
.c { flex-grow: 2; } /* 40 + 60 = 100px */03Take space back with flex-shrink
Now two items have bases of 200px and 100px in the same 240px row: 60px too much. Both have flex-shrink: 1, but shrinking is weighted by shrink × basis (200 and 100), so the big one gives up 2/3 of the 60px (40px) and the small one 1/3 (20px). Final widths: 160 and 80px.
04Use the shorthand
flex: 1 sets grow 1, shrink 1 and basis 0%. Every flex: 1 item starts from nothing, so the whole container is shared equally: equal columns. flex: auto starts from the content size, so bigger content stays bigger.
.equal { flex: 1; } /* 1 1 0% - equal widths */
.natural { flex: auto; } /* 1 1 auto - content size + equal extra */
.fixed { flex: none; } /* 0 0 auto - never grows or shrinks */05Meet the minimum size
Put a 300px-wide table inside a flex: 1 column. The column won't go below 300px, because its min-width is auto = its min-content size. The row overflows the page.
Add min-width: 0 to the column. Now it shrinks to its share and the table overflows inside the column, where you can give it a scrollbar with overflow-x: auto.
.main {
flex: 1;
min-width: 0; /* allow shrinking below the content's width */
}
.main .table-wrap {
overflow-x: auto; /* the wide table scrolls inside */
}06Truncate long text with an ellipsis
A file name in a flex row should end in "…" instead of pushing the row wider. The text needs white-space: nowrap, overflow: hidden and text-overflow: ellipsis, and the flex item holding it needs min-width: 0 (if the text is in a child of the item). Without it, the item stays as wide as the whole name and there's nothing to cut.
.file { display: flex; gap: 8px; }
.file .name {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}Try it yourself
- 1
Predict a grow split
In the first example, change the
.growrow's last item toflex-grow: 4. Predict the three widths first. (Total grow 6, 120px free, so 20px per share: 60, 60, 120.) - 2
Shrink twice as fast
In the second example, give the
.weightedrow's second itemflex-shrink: 4. Predict the widths. (Weights become 200 × 1 = 200 and 100 × 4 = 400, so it loses 40px of the 60 and the first loses 20: 180 and 60.) - 3
Cap a growing item
In the third example, add
[data-flex="1"] span:first-child { max-width: 50px; }. Predict the widths: the first item is frozen at 50px and the rest of the space goes to the second (190px).
Code & diagrams
Row 1: nothing grows; 120px stays free. Row 2: 120px free, split 1:1:2, so +30, +30, +60. Row 3: the last basis is 120px, so only 40px is free: +10, +10, +20. A grow of 2 means twice the **extra**, not twice the width: in row 3 the last item is 140px, not twice 50.
Preview
Console
Nothing logged yet.Expected console output
none: 40 + 40 + 40 = 120
grow: 70 + 70 + 100 = 240
big: 50 + 50 + 140 = 240Equal bases lose equal amounts (40px each). Bases 200 and 100 lose 40 and 20, in the ratio 200:100, so the bar keeps its proportions. With `flex-shrink: 0` on the first item, the second item gives up the whole 60px.
Preview
Console
Nothing logged yet.Expected console output
equal: 80, 80, 80
weighted: 160, 80
keep: 200, 40The contents are 20px and 100px wide. `none` keeps the content sizes. `auto` starts from them and shares the 120px of free space equally (+60 each). `1` starts both at 0 and shares all 240px, so the columns are equal.
Preview
Console
Nothing logged yet.Expected console output
flex: none -> 20, 100
flex: auto -> 80, 160
flex: 1 -> 120, 120In row 1 the `flex: 1` column can't go below its min-content width (300px), so it pokes 120px out of the dashed container. With `min-width: 0` it takes its fair share: 240 - 60 = 180px. Row 3 is the same fix used to truncate a long file name with "…". `getComputedStyle` reports `auto` for the default.
Preview
Console
Nothing logged yet.Expected console output
without min-width: 0 -> 300
with min-width: 0 -> 180
min-width values: auto and 0pxBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
A long URL breaks the layout
In a two-column row (aside fixed, main { flex: 1; }), paste a very long URL with no spaces into main.
Break #2
Expect width to win over flex-basis
Give an item width: 100px; flex-basis: 50px; in a row with no free space to share.
Break #3
flex: 1 columns of unequal width
In the third example, put a 160px-wide child inside the first flex: 1 item.
Myth vs fact
Myth
flex-grow: 2 makes an item twice as wide as a flex-grow: 1 item.
Fact
It gets twice as much of the free space. Final widths are basis plus extra, so they are only in a 2:1 ratio when the bases are 0, as with flex: 2 and flex: 1.
Myth
flex-shrink: 1 items all lose the same number of pixels.
Fact
Shrinking is weighted by shrink × basis, so bigger items lose more pixels.
Myth
flex: 1 and flex-grow: 1 are the same.
Fact
flex-grow: 1 keeps the basis at auto (content size). flex: 1 also sets the basis to 0%, which gives equal sizes.
Myth
A flex item can always shrink to fit.
Fact
Not below its automatic minimum size, which is its min-content size. Set min-width: 0 to allow it.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
The spec's algorithm (Flexbox §9.7, "Resolving Flexible Lengths") is a loop: compute free space, distribute it, clamp items that violate min/max constraints, freeze them, repeat. It uses the scaled flex shrink factor (shrink × inner flex base size) for negative space. If the sum of grow factors is less than 1, items take only that fraction of the free space:
flex-grow: 0.5on the only item fills half of it. - ▸
The automatic minimum size is the smaller of the item's content size suggestion and any specified size (like
width). It applies only when the item'soverflowisvisible(orclip); scroll containers get a minimum of 0, which is whyoverflow: hiddenalso fixes the gotcha. In a column, the same applies tomin-height, which commonly breaks scrolling panels inside a full-height column layout. - ▸
Watch out for percentage
flex-basisin a container with no definite size (for example a column flex container withheight: auto): the percentage can't resolve and behaves likecontent.flex: 1uses0%rather than0px, which matters in exactly that case. - ▸
Grow and shrink cause a lot of measuring: with
flex-basis: auto, the browser must lay out each item's content to find its size, and nested flex containers can multiply that work. On very long lists, giving items a definite basis (or using Grid with fixed tracks) reduces layout cost.
Remember this
- 1
flex-basisis an item's starting size on the main axis, before any growing or shrinking. Its initial value isauto, which means "use mywidth" in a row (orheightin a column), and if that is alsoauto, "use my content's size". A length likeflex-basis: 200pxwins overwidthon the main axis. Percentages are a share of the container's main size.flex-basis: 0means "start from nothing and get everything from growing". - 2
flex-growis a number (default0) that says how big a share of the positive free space an item takes. The browser adds up the grow numbers of all items in the line, and each item getsits grow ÷ total growof the free space, added to its basis. With grow values 1, 1 and 2 and 120px free, the items get 30px, 30px and 60px extra.flex-grow: 2does not mean "twice as wide", only "twice as much extra". With every grow at0, nothing grows and the free space stays forjustify-content. - 3
flex-shrinkis a number (default1) used when the items' bases add up to more than the container: the negative free space (the overflow) must be taken back. Shrinking is weighted by shrink × basis, not by the shrink number alone, so a 200px item loses twice as many pixels as a 100px item with the sameflex-shrink. That keeps the items' proportions.flex-shrink: 0means "never shrink me", which is what you want for logos, icons and fixed sidebars. - 4
The **
flexshorthand** sets all three at once:flex: <grow> <shrink> <basis>. Learn the four common keywords.flex: initialis0 1 auto(the default: don't grow, may shrink).flex: autois1 1 auto(start from content size, then share the extra).flex: noneis0 0 auto(fixed at content size).flex: 1is1 1 0%: one number sets grow, and the basis becomes 0, so all the space is shared by grow numbers alone andflex: 1items come out equal no matter their content.flex: 200pxmeans1 1 200px. Prefer the shorthand: its defaults for the missing parts are the useful ones. - 5
Items can't shrink below their minimum size. For flex items,
min-width(in a row) defaults toauto, and for an item withoverflow: visiblethatautomeans its min-content size: the width of its longest unbreakable thing, such as a long word, a URL, an image, a<pre>block or a child with a fixed width. This is the most common Flexbox bug: aflex: 1column refuses to shrink and the row overflows the screen. The fix ismin-width: 0(ormin-height: 0in a column), oroverflow: hidden, which also turns the automatic minimum off. - 6
The browser repeats the sharing until it's stable: if growing or shrinking pushes an item past its
min-widthormax-width, that item is frozen at the limit and the remaining free space is shared again between the others. That's howflex: 1; max-width: 300pxitems stop growing and hand the rest to their neighbours.
Explain it without notes
Explain flex-grow, flex-shrink and flex-basis in your own words.
What do flex: 1, flex: auto and flex: none expand to, and how do they differ?
How does the browser decide how many pixels each item loses when the items overflow?
Why does a flex: 1 column sometimes refuse to shrink, and how do you fix it?
Practice
Make a row with a 200px sidebar that never shrinks and a main area that takes all remaining space.
Make three buttons share a toolbar equally, whatever their labels.
In a chat list, each row has a 40px avatar and a .preview with a long last message. Make the message cut off with "…" on one line.
Trade-offs
- ↔
flex: 1gives equal sizes and ignores content, which suits columns and toolbars;flex: autorespects content, which suits navigation links of different lengths. Pick by whether the content or the grid should decide. - ↔
min-width: 0fixes overflow but then the content must handle the squeeze itself (wrap, truncate or scroll). Truncation hides information, so give the full text atitleattribute or a way to see it.
Done when you can
I can say what
flex-basisstarts from when it isauto.I can calculate final widths from bases, grow factors and the container width.
I can calculate how pixels are taken back with
flex-shrink.I know what
flex: 1,flex: auto,flex: noneandflex: 200pxexpand to.I can spot and fix the
min-width: 0gotcha.I can truncate text with an ellipsis inside a flex row.