Command Palette

Search for a command to run...

Hectal
PHASE 7Intermediate ~31 min· topic 3 of 5

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. auto means 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, like flex: 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.

style.csswhole filecss
.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.

style.csswhole filecss
.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.

Take space back with flex-shrinkdiagram
Rendering diagram…

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.

style.csswhole filecss
.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.

style.csswhole filecss
.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.

style.csswhole filecss
.file { display: flex; gap: 8px; }
.file .name {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

Try it yourself

  1. 1

    Predict a grow split

    In the first example, change the .grow row's last item to flex-grow: 4. Predict the three widths first. (Total grow 6, 120px free, so 20px per share: 60, 60, 120.)

  2. 2

    Shrink twice as fast

    In the second example, give the .weighted row's second item flex-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. 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

flex-grow shares out the free space New tab

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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

none: 40 + 40 + 40 = 120
grow: 70 + 70 + 100 = 240
big: 50 + 50 + 140 = 240
flex-shrink is weighted by the basis New tab

Equal 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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

equal: 80, 80, 80
weighted: 160, 80
keep: 200, 40
flex: none, flex: auto and flex: 1 New tab

The 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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

flex: none -> 20, 100
flex: auto -> 80, 160
flex: 1 -> 120, 120
The min-width: 0 gotcha New tab

In 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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

without min-width: 0 -> 300
with min-width: 0    -> 180
min-width values: auto and 0px

Break 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.

terminal
$ Preview
── what you'll see ──
The page gets a horizontal scrollbar; the main column is as wide as the URL and runs off the right edge of the screen.

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.

terminal
$ Console
── what you'll see ──
The item measures 50px, not 100px.

Break #3

flex: 1 columns of unequal width

In the third example, put a 160px-wide child inside the first flex: 1 item.

terminal
$ Console
── what you'll see ──
flex: 1 -> 160, 80

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.5 on 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's overflow is visible (or clip); scroll containers get a minimum of 0, which is why overflow: hidden also fixes the gotcha. In a column, the same applies to min-height, which commonly breaks scrolling panels inside a full-height column layout.

  • ▸

    Watch out for percentage flex-basis in a container with no definite size (for example a column flex container with height: auto): the percentage can't resolve and behaves like content. flex: 1 uses 0% rather than 0px, 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. 1

    flex-basis is an item's starting size on the main axis, before any growing or shrinking. Its initial value is auto, which means "use my width" in a row (or height in a column), and if that is also auto, "use my content's size". A length like flex-basis: 200px wins over width on the main axis. Percentages are a share of the container's main size. flex-basis: 0 means "start from nothing and get everything from growing".

  2. 2

    flex-grow is a number (default 0) 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 gets its grow ÷ total grow of 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: 2 does not mean "twice as wide", only "twice as much extra". With every grow at 0, nothing grows and the free space stays for justify-content.

  3. 3

    flex-shrink is a number (default 1) 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 same flex-shrink. That keeps the items' proportions. flex-shrink: 0 means "never shrink me", which is what you want for logos, icons and fixed sidebars.

  4. 4

    The **flex shorthand** sets all three at once: flex: <grow> <shrink> <basis>. Learn the four common keywords. flex: initial is 0 1 auto (the default: don't grow, may shrink). flex: auto is 1 1 auto (start from content size, then share the extra). flex: none is 0 0 auto (fixed at content size). flex: 1 is 1 1 0%: one number sets grow, and the basis becomes 0, so all the space is shared by grow numbers alone and flex: 1 items come out equal no matter their content. flex: 200px means 1 1 200px. Prefer the shorthand: its defaults for the missing parts are the useful ones.

  5. 5

    Items can't shrink below their minimum size. For flex items, min-width (in a row) defaults to auto, and for an item with overflow: visible that auto means 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: a flex: 1 column refuses to shrink and the row overflows the screen. The fix is min-width: 0 (or min-height: 0 in a column), or overflow: hidden, which also turns the automatic minimum off.

  6. 6

    The browser repeats the sharing until it's stable: if growing or shrinking pushes an item past its min-width or max-width, that item is frozen at the limit and the remaining free space is shared again between the others. That's how flex: 1; max-width: 300px items stop growing and hand the rest to their neighbours.

Explain it without notes

01

Explain flex-grow, flex-shrink and flex-basis in your own words.

02

What do flex: 1, flex: auto and flex: none expand to, and how do they differ?

03

How does the browser decide how many pixels each item loses when the items overflow?

04

Why does a flex: 1 column sometimes refuse to shrink, and how do you fix it?

Practice

01

Make a row with a 200px sidebar that never shrinks and a main area that takes all remaining space.

02

Make three buttons share a toolbar equally, whatever their labels.

03

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: 1 gives equal sizes and ignores content, which suits columns and toolbars; flex: auto respects content, which suits navigation links of different lengths. Pick by whether the content or the grid should decide.

  • ↔

    min-width: 0 fixes overflow but then the content must handle the squeeze itself (wrap, truncate or scroll). Truncation hides information, so give the full text a title attribute or a way to see it.

Done when you can

  • I can say what flex-basis starts from when it is auto.

  • 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: none and flex: 200px expand to.

  • I can spot and fix the min-width: 0 gotcha.

  • I can truncate text with an ellipsis inside a flex row.