How do flex-grow, flex-shrink, and flex-basis interact to determine an item flex size?

Intermediate15 min interview
Skills tested:
Identifying flex-basis as the starting size before distributionComputing grown widths from proportional free space distributionComputing shrunk widths using the size-weighted shrink formulaKnowing the shorthand expansions of flex: 1, flex: auto, and flex: noneExplaining the difference between flex-basis: 0 and flex-basis: auto

Advertisement

🧩 Scenario

This trio is the actual sizing engine of Flexbox, and it is where most Flexbox confusion lives. A sidebar that should stay fixed while the main panel absorbs the remaining width, a row of cards that must be exactly equal regardless of content length, a toolbar whose buttons must never squash below their labels: all three are expressed entirely through flex-basis, flex-grow, and flex-shrink. Reading a layout means being able to run the algorithm mentally.

Architecture Walkthrough

Step 1: flex-basis Sets the Starting Size

Before any growing or shrinking happens, each item gets a base main-axis size from flex-basis. This is the hypothetical size the algorithm starts from.

flex-basis: auto, the initial value, defers to the item's width (or height in a column container). If that is also auto, the base size becomes the item's content size, its max-content width. flex-basis: 0 means start from nothing and let growth do all the work. Any length or percentage sets the base size directly, and percentages resolve against the container's main-axis size.

When both flex-basis and width are set, flex-basis wins for the flex sizing calculation. width becomes effectively a fallback for non-flex contexts.

Step 2: Measure Free Space

The browser sums the base sizes of all items, adds any gap, and compares that total to the container's main-axis size.

If the container is larger, there is positive free space, and flex-grow distributes it. If the container is smaller, there is negative free space, and flex-shrink removes the overflow. Only one of the two ever applies in a given pass.

Step 3: Growing Is Unweighted and Proportional

flex-grow distributes free space in proportion to the grow factors, and the distribution applies only to the free space, not to the final size.

Take three items with flex-basis: 100px in a 600px container. Base total is 300px, so free space is 300px. The grow factors 1, 2, and 3 sum to 6, so the shares are 50px, 100px, and 150px. Final widths are 150px, 200px, and 250px.

This is the misconception worth killing: flex-grow: 2 does not make an item twice as wide as a flex-grow: 1 sibling. It gives it twice the share of the leftover space. The final widths in the example are 150 and 200, nowhere near a 1:2 ratio, because each item started from a 100px base.

The exception is when flex-basis: 0. With no base size, the free space is the entire container and the growth shares become the final sizes, so the ratios do come out exactly as the factors. That is precisely why flex: 1 uses a basis of 0%.

Step 4: Shrinking Is Weighted by Base Size

flex-shrink works differently, and the asymmetry is intentional. The shrink amount for each item is proportional to flex-shrink × base size, not to flex-shrink alone.

Consider items of 100px and 300px, both with flex-shrink: 1, in a 300px container. The overflow is 100px. The weighted factors are 1×100 = 100 and 1×300 = 300, totalling 400. The first item absorbs 100/400 of the overflow, which is 25px; the second absorbs 300/400, which is 75px. Final widths are 75px and 225px.

If shrinking were unweighted, both would lose 50px and the smaller item would be reduced to half its size while the larger lost only a sixth. Weighting by base size keeps the reduction proportionate so small items do not vanish first.

Step 5: Clamping

The computed sizes are then clamped by min-width, max-width, and their block-direction equivalents. The critical one is that flex items have min-width: auto rather than 0, meaning they will not shrink below their automatic minimum content size, generally the widest unbreakable word or the intrinsic width of a replaced element. This is the mechanism behind items refusing to shrink far enough and overflowing their container instead, and min-width: 0 is the standard override.

The Shorthands

Reading real code requires knowing the expansions, several of which are not obvious:

  • flex: 11 1 0%
  • flex: auto1 1 auto
  • flex: initial0 1 auto (the default)
  • flex: none0 0 auto
  • flex: 0 auto0 1 auto
  • flex: 22 1 0%

The flex: 1 versus flex: auto distinction matters constantly. flex: 1 starts every item from zero, so a row of cards ends up exactly equal regardless of how much text each contains. flex: auto starts from content size, so cards with more text end up wider even though all of them grow. Use flex: 1 for a strict grid feel and flex: auto when content should influence proportions.

flex: none is the tool for a sidebar or icon that must keep exactly its declared size: no growth, no shrinkage.


Key Code Explained

/* Step-by-step example: 600px container, three items */
.container {
  display: flex;
  width: 600px;
}
.a { flex: 1 1 100px; } /* base 100 + 50  = 150px */
.b { flex: 2 1 100px; } /* base 100 + 100 = 200px */
.c { flex: 3 1 100px; } /* base 100 + 150 = 250px */
/* free space = 600 - 300 = 300; factors 1+2+3 = 6; shares 50/100/150.
   NOTE: final widths are 150/200/250 — NOT a 1:2:3 ratio. */

/* With flex-basis: 0 the ratios DO come out as the factors */
.x { flex: 1 1 0; } /* 100px */
.y { flex: 2 1 0; } /* 200px */
.z { flex: 3 1 0; } /* 300px */

/* Shrinking is weighted by BASE SIZE — 300px container, 400px of content */
.small { flex: 0 1 100px; } /* 1x100=100 of 400 -> loses 25px -> 75px  */
.large { flex: 0 1 300px; } /* 1x300=300 of 400 -> loses 75px -> 225px */

/* flex: 1 vs flex: auto — the difference is the BASIS */
.equal-cards > * {
  flex: 1; /* 1 1 0%  -> all cards exactly equal, content ignored */
}
.content-cards > * {
  flex: auto; /* 1 1 auto -> cards with more text end up wider */
}

/* The classic app shell */
.shell {
  display: flex;
}
.sidebar {
  flex: none;       /* 0 0 auto: never grows, never shrinks */
  width: 240px;
}
.main {
  flex: 1;          /* absorbs every remaining pixel */
  min-width: 0;     /* allow shrinking below content size */
}

/* min-width: auto is the default on flex items and floors shrinking */
.will-overflow {
  flex: 1 1 0;
  /* a long unbreakable string keeps this wider than 0 and overflows */
}
.will-not-overflow {
  flex: 1 1 0;
  min-width: 0;     /* the standard override */
  overflow: hidden;
}

The first block is the one worth being able to derive on paper. It disproves the intuitive reading of flex-grow directly: the factors are 1, 2, and 3 but the widths come out 150, 200, and 250. Understanding that growth divides only the leftover 300px, on top of the 100px each item already had, is the difference between predicting a layout and adjusting numbers until it looks right.

The .shell block is the pattern to have memorised, because it is the skeleton of nearly every application layout: flex: none with an explicit width for the fixed rail, flex: 1 with min-width: 0 for the flexible region. The min-width: 0 is not optional in practice; without it, long content in the main panel prevents it from shrinking and the whole shell overflows.


Tradeoffs

ShorthandExpands toBehaviour
flex: 11 1 0%Equal sizes, content ignored
flex: auto1 1 autoGrows, but content influences proportions
flex: initial0 1 autoContent-sized, shrinks only
flex: none0 0 autoFixed at content or declared size
flex: 22 1 0%Twice the share of a flex: 1 sibling
flex: 0 auto0 1 autoSame as initial

What Interviewers Actually Check

  • Whether you identify flex-basis as the starting size before any distribution happens
  • Whether you know flex-grow divides only the free space, so factors are not final size ratios
  • Whether you know flex-shrink is weighted by base size and can explain why
  • Whether you can expand flex: 1, flex: auto, and flex: none correctly
  • Whether you know min-width: auto floors shrinking and can name the override

Follow-Up Questions

  1. How does gap participate in the free space calculation, and does it shrink when space runs out?
  2. What happens if flex-basis is a percentage and the container's main-axis size is indefinite?
  3. Why do flex items default to min-width: auto rather than 0, given how often it needs overriding?
  4. How does the flex sizing algorithm handle an item whose max-width clamps it during growth? Does the leftover space redistribute?
  5. How would you achieve the equivalent of flex: 1 behaviour in CSS Grid, and which is more appropriate for a row of equal cards?

Common Candidate Mistakes

  • Claiming flex-grow: 2 makes an item twice as wide as a flex-grow: 1 sibling, when the factors only divide the leftover free space on top of each item's base size
  • Using flex: 1 and flex: auto interchangeably, then being surprised that one produces exactly equal cards and the other lets content length change the widths
  • Forgetting that flex-shrink is weighted by base size, and predicting that two items with the same shrink factor lose the same number of pixels
  • Setting width and expecting it to control flex sizing, when flex-basis takes precedence in the flex layout algorithm
  • Overlooking the default min-width: auto, which stops items shrinking below their content size and causes container overflow that looks like a flex-shrink failure

Interview Readiness Checklist

Before you leave this question, make sure you can answer:

  • Can you explain what flex-basis establishes before any growing or shrinking happens?
  • Can you compute final widths for three items with different flex-grow values and a shared non-zero basis?
  • Can you explain why flex-shrink is weighted by base size and what problem that solves?
  • Can you expand flex: 1, flex: auto, flex: none, and flex: initial from memory?
  • Can you explain the practical difference between flex-basis: 0 and flex-basis: auto for a row of cards?

Summary

Flex sizing runs in stages. flex-basis establishes each item's starting main-axis size, defaulting to auto which defers to width and then to content size. The browser sums those base sizes plus gaps and compares the total to the container: a surplus means flex-grow distributes free space, a deficit means flex-shrink removes overflow, and only one of the two ever applies.

flex-grow divides the free space in proportion to the grow factors, on top of each item's base size. That is why three items with basis 100px and factors 1, 2, 3 in a 600px container end up 150px, 200px, and 250px rather than in a 1:2:3 ratio. The ratios only match the factors when the basis is zero, which is exactly why flex: 1 expands to 1 1 0%. flex-shrink, by contrast, is weighted by base size, so a 300px item absorbs three times as much reduction as a 100px item at the same shrink factor. That weighting exists so small items do not disappear before large ones.

Finally, results are clamped by min and max constraints, and flex items default to min-width: auto, which prevents shrinking below content size and is the usual cause of stubborn overflow. In practice the shorthands carry most of the meaning: flex: 1 for strictly equal items, flex: auto when content should influence proportions, flex: none for a fixed rail, and min-width: 0 on any flexible region that must be allowed to shrink.

Frequently Asked Questions

What does flex: 1 expand to?

flex-grow: 1, flex-shrink: 1, flex-basis: 0%. Note the basis is 0%, not auto, which is why flex: 1 produces equal-width items regardless of content.

Why is flex-shrink weighted by base size but flex-grow is not?

Shrinking proportionally to size prevents small items from disappearing. If shrinking were unweighted, a 50px item and a 500px item would each lose the same number of pixels and the small one would vanish first.

Advertisement


Stay Updated

Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.

Advertisement