skip to content

Flex Sizing Pitfalls

The bugs everyone eventually hits: min-width: auto refusing to shrink, text blowing out a flex child instead of truncating, percentage heights, and stretched images. These separate people who have shipped flex layouts from people who have only read the spec.

part ofCSSoverview, primer and where to startread it →
on this pageshow

questions

5

Why does a flex item often refuse to shrink below the width of its content, and what does setting min-width: 0 on it change?

level: middleimportance: must knowfreq 66%

answer

  1. the item has a floor you did not set
  2. initial value is auto, not zero
  3. content-based minimum on the main axis
  4. overflow other than visible zeroes it
  5. min-width: 0 in a row, min-height: 0 in a column

basics

~20 s

Flex items default to min-width: auto, which floors their main size at roughly their min-content size, so long content overflows the container instead of shrinking. Setting min-width: 0, or any overflow value other than visible, removes that floor.

solid answer

~50 s

On a flex item, `min-width` and `min-height` keep their initial value of `auto`, and for a flex item `auto` means the **automatic minimum size** — essentially the item's min-content size, the width of its longest unbreakable chunk of content. So `flex-shrink` can shrink the item down to that floor and no further, which is why a long URL, a wide table, or a `<pre>` block pushes its flex item past the container edge instead of compressing. The fix is to explicitly release the floor on the main axis: `min-width: 0` in a row container, `min-height: 0` in a column one. Giving the item a non-`visible` overflow (`overflow: hidden`, `auto`, `scroll`) does the same thing implicitly, because the spec zeroes the automatic minimum size when the item is itself a scroll container. Both are legitimate; pick `min-width: 0` when you do not actually want a scroll container.

code

css · 15 lines
css
.row {
  display: flex;
  width: 300px;
}

/* Overflows: flex-shrink stops at the item's min-content width. */
.row > .broken {
  flex: 1;
}

/* Shrinks properly: the content-based floor is released. */
.row > .fixed {
  flex: 1;
  min-width: 0;
}

go deeper

for a junior

Recognise the symptom and the one-line cure: when a flex item overflows instead of shrinking, add min-width: 0 to it. Say plainly that the item has a default minimum you did not write.

for a middle

Explain that min-width's initial value is auto, that auto means the content-based automatic minimum size on a flex item, and that a non-visible overflow value zeroes it. Get the axis right for column containers.

for a senior

Show how you diagnose it in devtools from the computed min-width, and explain why the default exists — visible overflow beats silently crushed content. Know that nested flex chains need the release at every level.

for a principal

Frame it as an API-design tradeoff the spec made deliberately, and set a team convention: whether shrinkable layout primitives ship with min-width: 0 baked in, and how you keep that from silently clipping content elsewhere.

## The symptom You build a two-column flex row, give both children `flex: 1`, and expect them to split the space evenly. Instead one column is far wider than the other, or the row visibly overflows its parent and a horizontal scrollbar appears on the page. Nothing you do with `flex-shrink` helps. The cause is almost always the flex item's **automatic minimum size**. ## What min-width: auto means In CSS, the initial value of `min-width` and `min-height` is `auto`. For an ordinary block box, `auto` resolves to `0` — that is why you never notice it. For a **flex item** (and a grid item), `auto` resolves to the automatic minimum size instead: a content-based floor, roughly the box's **min-content size**. The min-content size is the narrowest the box can be without its content overflowing: for text it is the width of the longest word; for a long unbroken URL it is the whole URL; for a replaced element such as an image it is derived from the intrinsic size; for a nested layout it is that layout's own min-content size. So the practical rule is: *a flex item will not shrink below the width of its widest unbreakable content, no matter what flex-shrink says.* ```css .row { display: flex; width: 300px; } .item { flex: 1; } /* wants 150px, but cannot go below min-content */ ``` If `.item` contains `supercalifragilisticexpialidocious` at 20px, its min-content width might be 340px, and the row overflows by 40px — flex layout honoured the minimum and let the overflow happen. ## The two fixes **1. Zero the minimum explicitly.** ```css .item { flex: 1; min-width: 0; } /* row direction */ .item { flex: 1; min-height: 0; } /* column direction */ ``` The axis matters: the automatic minimum size applies on the item's **main axis**, so in `flex-direction: column` the offending property is `min-height`, not `min-width`. In a row container `min-height: auto` is harmless; in a column container it is the whole bug. **2. Make the item a scroll container.** The spec says the automatic minimum size is zero when the item's computed `overflow` in that axis is anything other than `visible`. So `overflow: hidden`, `overflow: auto`, and `overflow: scroll` all silently release the floor: ```css .item { flex: 1; overflow: hidden; } ``` This is why adding `overflow: hidden` "magically fixes" flex overflow bugs, and why people cargo-cult it. Prefer `min-width: 0` when you do not want clipping or scrolling as a side effect; use `overflow: hidden` when you were going to clip anyway (a truncating label, for example). ## Why the rule exists It is a deliberate safety default, not an oversight. Before it, `flex-shrink` would happily crush items to zero width and content would vanish behind the container edge with no scrollbar and no clue. The content-based floor makes the overflow *visible*, so you notice the layout does not fit rather than silently losing content. The tradeoff is that when you genuinely want truncation or an inner scroller, you have to say so. ## Where it bites in practice - **Truncation.** `text-overflow: ellipsis` on a child inside a flex item does nothing until the item is allowed to be narrower than the text. - **Nested flex.** A flex item that is itself a flex container inherits the problem from *its* children: the outer item's min-content size is computed from the inner layout, so a single wide grandchild can hold an entire column chain open. You often need `min-width: 0` on every level. - **Tables and `<pre>`.** Both have large min-content sizes, so they are classic culprits inside cards and sidebars. - **Grid too.** Grid items have the same `auto` minimum, which is why `1fr` tracks blow out — `minmax(0, 1fr)` is the grid-side equivalent of `min-width: 0`. ## How to diagnose it quickly Inspect the offending item and look at its *computed* `min-width`. If it reads `auto` and the item is a flex item, that is your answer. A fast confirmation: temporarily set `min-width: 0` in devtools and watch the layout snap into place. A second tell is that the overflow is exactly as wide as one long word or one wide descendant — the number matches the min-content size, not some fraction of the container. The habit worth forming: when a flex layout overflows, do not reach for `flex-shrink`, `width: 100%`, or `max-width` first. Ask what the item's minimum size is, and release it on the main axis.

  • Does the same problem exist in a column flex container, and which property do you reach for there?
    Yes — the automatic minimum size applies on the main axis, so in `flex-direction: column` the offending declaration is `min-height: auto` and the fix is `min-height: 0`. This is the reason a nested scrollable panel in a column layout refuses to scroll: every intermediate item is held open by its content.
  • Why does adding overflow: hidden fix a flex overflow bug even though you never touched min-width?
    Because the spec zeroes a flex item's automatic minimum size when the item's computed overflow in that axis is not `visible`. Making the item a scroll container implies it can be smaller than its content, so the content-based floor no longer applies. It is a real fix, but it also clips, which may not be what you want.
  • Is there an equivalent trap in CSS Grid?
    Yes. Grid items also default to `min-width: auto` / `min-height: auto`, so a `1fr` track refuses to go below the item's min-content size and the grid overflows. The idiomatic fixes are `minmax(0, 1fr)` on the track or `min-width: 0` on the item.

Flex shrinking is like squeezing passengers onto a bench: they compress until shoulders touch, and shoulder width is the min-content size. min-width: 0 tells the browser the passengers may overlap.

saying these in an interview costs you the question

  • Claiming flex-shrink: 1 alone guarantees an item can reach any width
  • Thinking min-width defaults to 0 on flex items like on block boxes
  • Fixing row overflow with min-height: 0 instead of min-width: 0
  • Believing overflow: hidden works by clipping rather than by zeroing the minimum
  • Assuming the floor comes from flex-basis or width rather than content

context

open as a page

An <img> placed directly inside a display: flex row container renders taller and visibly distorted instead of at its natural size. What causes that, and how do you fix it?

level: juniorimportance: should knowfreq 47%

basics

~20 s

A flex container stretches items along the cross axis by default, so an image with no set height is stretched to the height of the tallest item on the line while its width stays intrinsic, distorting it. Stop the stretch with align-self: flex-start or an explicit size.

open as a page

What does it take to make a single line of text truncate with text-overflow: ellipsis when that text lives inside a flex item?

level: middleimportance: should knowfreq 52%

basics

~20 s

Three declarations on the block element holding the text — overflow: hidden, white-space: nowrap, text-overflow: ellipsis — plus min-width: 0 on every row-direction flex item above it, so the box is allowed to be narrower than the text.

open as a page

In an app shell built from nested flex-direction: column containers, a panel with overflow-y: auto never scrolls — the whole page grows instead. What causes this and how do you fix it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Each column flex item defaults to min-height: auto, so it cannot be shorter than its content and grows instead of bounding the scroller. Add min-height: 0 (or a non-visible overflow) on every flex item between the height-constrained ancestor and the scrolling panel.

open as a page

How does a child with position: absolute behave inside a display: flex container — is it still a flex item?

level: middleimportance: nice to knowfreq 27%

basics

~20 s

No. An absolutely positioned child is out of flow, so it is not a flex item: it takes no space, is ignored by free-space distribution, and flex-grow, flex-shrink, flex-basis and order do nothing to it. Alignment still sets its static position.

open as a page