min-height is breaking your flexbox layout
You’ve built the classic app shell. Top bar across the top, sidebar down the left, and a big content pane. You give the content pane overflow-auto, so that it scrolls instead of the whole page scrolling away your layout.
And yet…the whole page still scrolls. The top bar scrolls right off the screen and the content pane’s scrollbar never shows up even though every element in the chain has height: 100%.
It turns out that by default, every flex item in a column layout sneakily has
min-height: auto;For the purposes of our layout, this effectively means
“I will not become shorter than my content’s intrinsic height, no matter what the flex algorithm says.
<style> html, body { height: 100%; }</style>
<div data-name="shell" x class="flex h-full flex-col"> <header>top bar</header>
<div data-name="row" class="flex flex-1"> <nav>sidebar</nav> <main class="h-full overflow-auto">700px of table</main> </div></div>-
The shell is
height: 100%, viewport height. -
The below header
flex-1which containsflex-grow: 1, so the flex algorithm wants to size it vertically to “viewport minus top bar”. -
But because
main(and thusrow) is taller than the viewport, there is a conflict:- The implicit
min-height: autoofrowmeans that its minimum height is its content height, which in this case is the 700px tall main - The
flex-grow + flex-basisofrowshould make it fit perfectly below header and next to sidebar by growing to fit the space.
- The implicit
-
min-heightwins the argument againstflex-basisrowgrows until its height fitsmainmain’sheight: 100%resolves againstrowthat already grew to fit the content1, so “100%” is big enough to hold everything.main’soverflow-autonever fires
-
This all results in the shell overflowing the viewport and the browser scrolling the entire layout away.
<div data-name="row" class="flex flex-1 min-h-0"></div>min-h-0 replaces min-height: auto with min-height: 0, telling the row “you’re allowed to be smaller than your content.”
That caps the row’s height at the remaining viewport height, and every height below it snaps into place. main’s h-full now means “row height” instead of “content height”, and the overflow lands where we already handled it: main’s overflow-auto scroll container.
That whole example was about a child of a flex column, but the same is true regardless of flex-direction.
In a flex-direction: row layout the same default shows up as min-width: auto, and its often resulting in text truncation. If you’ve ever stacked truncate on a flex child and watched it stubbornly not truncate while blowing out the layout: it’s this exact bug rotated 90 degrees. min-width: 0 is the fix.
You often need a panel that grows to fill available space, but stops growing once it runs out of content, and scrolls only when it has to.
flex: 1;min-height: 0;max-height: max-content;overflow-y: auto;max-height: max-content caps the panel at “as tall as my content wants to be”, so it never greedily hoards empty space. It takes room while it needs it, stops when it doesn’t, and scrolls when it can’t get enough.
“It doesn’t want to scroll, but it will if it has to.”
Any time an height: 100% / overflow: auto chain passes through a flex item, that item needs min-height: 0 (or overflow: hidden).
-
This is cyclical because the content that the row grows to fit is main, and the parent that main’s
height: 100%is growing to fit is the row. ↩ -
Per the flexbox spec, the
autominimum only resolves to the content-based minimum whenoverflowisvisible; otherwise it’s zero. ↩