Why might flex-shrink cause content to overflow instead of shrinking as expected?
Advertisement
🧩 Scenario
Architecture Walkthrough
flex-shrink Is Not the Floor
flex-shrink describes the rate at which an item gives up space, not the limit of how small it can get. That limit comes from the item's minimum size, and the two are resolved independently: the flex algorithm computes a shrunk size, then clamps it against min-width and max-width.
If the clamp raises the size back up, the sum of the items exceeds the container and the row overflows. flex-shrink: 1 was honoured, the algorithm ran correctly, and the result was overridden by a minimum. That is the entire mechanism behind the bug.
The Automatic Minimum Size
Here is the default nobody expects: flex items have min-width: auto on the main axis, not min-width: 0. In non-flex layout, min-width: auto computes to 0, which is why this behaviour catches people out; the flex spec gives auto a different, non-zero meaning called the automatic minimum size.
For most items, that automatic minimum resolves to the item's min-content size: the narrowest the content can be without overflowing its own box. Concretely, for a block of text it is the width of the longest unbreakable word, so a cell containing "internationalization" cannot shrink below the rendered width of that word. For a replaced element such as an image or a video, it is the intrinsic size, so a 900px image sets a 900px floor unless something constrains it. For an item that is itself a scroll container, the automatic minimum is zero, which is why adding overflow: hidden also fixes the bug.
In a column container the same rule applies on the block axis via min-height: auto.
Why This Breaks Truncation Specifically
text-overflow: ellipsis requires three things: overflow: hidden, white-space: nowrap, and a constrained width. In a flex row, the third condition is the one that silently fails.
white-space: nowrap makes the entire string one unbreakable unit, which means the item's min-content size becomes the full width of the text. The automatic minimum then pins the item to that width. The item never shrinks, the width is never constrained, and there is nothing for the ellipsis to trigger on, so the text simply overflows. Every declaration in the truncation recipe is present and correct; the missing piece is min-width: 0.
Nested Containers Propagate the Minimum
This is the part that turns a one-line fix into a debugging session. A flex item that is itself a flex container has its own automatic minimum derived from its children's minimums. That value propagates up the chain: if the innermost text item has a 400px minimum, its flex parent inherits a 400px minimum, and so does that parent's parent.
Consequently min-width: 0 on the innermost item alone is not enough. It has to be applied at every level between the text and the container that should constrain the width. In application shells three or four levels deep, this is why the fix seems not to work at first: it was applied in one place and the floor is being set somewhere else in the chain.
Other Causes of Flex Overflow
Not every flex overflow is a minimum-size problem, so rule these out too.
flex-shrink: 0 on any item, which is what flex: none sets, exempts it from shrinking entirely. One such item in a row can push the total past the container width even when every other item shrinks as far as it can.
An explicit min-width in pixels does exactly what it says and floors shrinking at that value. A flex-basis or width larger than the container combined with flex-shrink: 0 overflows immediately. Padding and border on the item add to its size and are not removed by shrinking under content-box. And gap consumes main-axis space that is not available to the items, so a row with many items and a large gap can overflow even when every item is fully shrunk.
The Fixes
min-width: 0 on the flex item is the primary fix, applied at every nesting level that needs to shrink. overflow: hidden works too, because making the item a scroll container sets its automatic minimum to zero, and it is often wanted anyway for truncation. flex-basis: 0 reduces the starting size but does not change the minimum, so it is not a substitute. overflow-wrap: anywhere or word-break: break-word lowers the min-content size itself by allowing breaks inside words, which is the right fix when the content is a long URL or hash that should wrap rather than truncate.
Key Code Explained
/* THE BUG: every truncation declaration is present, and it still overflows */
.row {
display: flex;
gap: 12px;
}
.row .filename {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
/* nowrap makes the whole string one unbreakable unit, so
min-width: auto floors this item at the full text width */
}
/* THE FIX: one declaration */
.row .filename-fixed {
flex: 1;
min-width: 0; /* overrides the automatic minimum size */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* overflow: hidden alone also works, because a scroll container's
automatic minimum size is zero */
.also-works {
flex: 1;
overflow: hidden;
}
/* NESTED: the minimum propagates UP, so every level needs the override */
.shell {
display: flex;
}
.shell .panel {
flex: 1;
min-width: 0; /* level 1 */
display: flex;
}
.shell .panel .content {
flex: 1;
min-width: 0; /* level 2 — without this the fix appears not to work */
display: flex;
}
.shell .panel .content .label {
min-width: 0; /* level 3 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Column containers use the block axis */
.column-shell {
display: flex;
flex-direction: column;
height: 400px;
}
.column-shell .scroll-region {
flex: 1;
min-height: 0; /* the column-axis equivalent; without it, no inner scroll */
overflow-y: auto;
}
/* Replaced elements: intrinsic size sets the floor */
.media-row img {
min-width: 0;
max-width: 100%; /* a 900px image otherwise floors the item at 900px */
}
/* Not a minimum-size problem: flex-shrink: 0 exempts an item entirely */
.toolbar .fixed-button {
flex: none; /* 0 0 auto — will never shrink, can overflow the row */
}
/* Lower the min-content size itself when the content should wrap */
.url-cell {
flex: 1;
min-width: 0;
overflow-wrap: anywhere; /* long URLs break instead of setting a wide floor */
}
The .row .filename versus .filename-fixed pair is the single most valuable thing in this question, because the broken version is what everybody writes. All three truncation ingredients are there and correct. The only difference is min-width: 0, and without it white-space: nowrap actively works against you by turning the whole string into one unbreakable unit that sets the item's floor.
The nested block matters just as much in real applications. Applying min-width: 0 to the text element alone and seeing no change is the normal experience, and it leads people to conclude the fix does not work. The minimum is computed bottom-up through every flex container in the chain, so the override has to appear at each level that must be allowed to shrink.
Tradeoffs
| Fix | Mechanism | Side effects |
|---|---|---|
min-width: 0 | Overrides the automatic minimum | None; the standard fix |
overflow: hidden | Scroll container has a zero automatic minimum | Clips descendants, may cut shadows or popovers |
overflow-wrap: anywhere | Lowers the min-content size itself | Words break mid-character |
flex-basis: 0 | Lowers the starting size only | Does not change the minimum; not a fix |
Explicit max-width on a child image | Caps the intrinsic contribution | Only helps for replaced elements |
What Interviewers Actually Check
- Whether you know flex items default to
min-width: autorather than0 - Whether you can say what the automatic minimum resolves to for text and for replaced elements
- Whether you connect
white-space: nowrapto a raisedmin-contentsize and thus to failing truncation - Whether you know the minimum propagates up through nested flex containers
- Whether you can name causes of flex overflow other than the automatic minimum
Follow-Up Questions
- Why does making an item a scroll container set its automatic minimum size to zero?
- Does
min-width: 0have any downside, and why is it not simply the spec default? - How does the equivalent situation behave in CSS Grid, where
minmax(0, 1fr)is the well-known counterpart? - What is the difference between
overflow-wrap: anywhereandword-break: break-allin terms of the computedmin-contentsize? - How would you debug this in devtools, given that the computed
min-widthshowsautorather than a pixel value?
Common Candidate Mistakes
- Treating
flex-shrink: 1as a guarantee that an item can shrink to any size, when the shrunk result is then clamped by the item's minimum - Not knowing flex items default to
min-width: auto, which in flex layout means the content-derived automatic minimum rather than zero - Adding
overflow: hiddento the flex container instead of the flex item, which hides the symptom by clipping rather than allowing the item to shrink - Applying
min-width: 0at only one level of a nested flex structure, where the minimum propagates up from the innermost content through every flex container in the chain - Overlooking non-minimum causes such as a
flex: nonesibling, an explicit pixelmin-width, padding undercontent-box, orgapconsuming main-axis space
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you explain what
min-width: autoresolves to on a flex item, and how that differs from non-flex layout? - Can you explain precisely why
min-width: 0fixes the overflow? - Can you explain why
text-overflow: ellipsisneedsmin-width: 0in a flex row? - Can you trace how the minimum propagates through nested flex containers?
- Can you name at least three causes of flex overflow other than the automatic minimum size?
Summary
flex-shrink controls the rate at which an item gives up space, not the smallest size it can reach. After the flex algorithm computes a shrunk size, that size is clamped by the item's minimum, and if the clamp raises it back up the container overflows. The declaration was honoured; the result was overridden.
The reason this happens by default is that flex items have min-width: auto on the main axis, and in flex layout auto means the automatic minimum size rather than zero. For text that resolves to the min-content width, the longest unbreakable word; for images and other replaced elements it resolves to the intrinsic size; and for an item that is itself a scroll container it resolves to zero. This is why text-overflow: ellipsis fails in flex rows: white-space: nowrap makes the whole string unbreakable, raising min-content to the full text width and pinning the item there so nothing ever constrains it.
The fix is min-width: 0 on the item, or min-height: 0 in a column container, and it must be applied at every nesting level between the content and the container that should constrain the width, because the minimum propagates upward through nested flex containers. overflow: hidden achieves the same thing by making the item a scroll container, and overflow-wrap: anywhere is the right choice when long strings should wrap rather than truncate. Before concluding it is a minimum-size problem, rule out a flex: none sibling, an explicit pixel min-width, content-box padding, and gap consuming space the items never had.
What is the single most common fix?
min-width: 0 on the flex item, or min-height: 0 in a column container. It overrides the automatic minimum size and lets flex-shrink work as expected.
Why does the spec default flex items to min-width: auto?
To stop content being unreadably squashed or clipped by default. It is a safety default that protects the common case and needs overriding in the flexible-panel case.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement