Skip to content
DevDepth
← Back to all articles

In-Depth Article

How to Fix Scroll Failures and Stretched UI in Flexbox and Grid

Use `min-height: 0`, inner scroll wrappers, auto margins, non-stretch alignment, and `flex-shrink: 0` to prevent broken scrolling and distorted UI in Flexbox and Grid.

Published: Updated: 7 min readlayout-strategy

Some Flexbox and Grid bugs feel random until you notice that the browser is doing exactly what the layout model asked it to do.

A scroll area refuses to scroll. A row aligned to the end hides part of its content. An icon or button stretches to match a taller neighbor. A compact action suddenly gets squashed because nearby text is too long. These do not usually come from broken layout engines. They come from a few defaults that are easy to forget when you are moving fast.

This guide focuses on the four patterns that cause the most confusion in production UI:

  • scroll regions inside flexible shells
  • overflow that becomes unreachable because of end alignment
  • default stretching that distorts controls and media
  • shrink pressure that squeezes UI you expected to stay fixed

If you want the deeper sizing background first, start with Why min-width: 0 Fixes So Many Flexbox and Grid Layout Bugs and Flexbox Sizing Explained: flex-grow, flex-shrink, flex-basis, and Why Width Is Not the Whole Story. If your main pain is content overflow more broadly, continue with How to Prevent Overflow in Flexbox and Grid Layouts.

1. These bugs are mostly defaults, not mysteries

Flexbox and Grid both come with sensible defaults, but those defaults are not always what a specific interface needs.

Common examples:

  • flexible children protect their content minimums
  • items stretch on the cross axis unless told otherwise
  • alignment can push overflow toward the start side instead of the scrollable side
  • flex items are allowed to shrink unless you explicitly protect them

The result is that a layout can look perfect in a simple demo and then fail the moment:

  • the viewport gets smaller
  • a component becomes scrollable
  • one line of text becomes much longer
  • one child grows taller than its siblings

That is why defensive layout code matters so much. You are not fighting the browser. You are making the layout contract more explicit.

2. Scroll containers fail when the flexible region is not allowed to shrink

One of the most common app-shell patterns is:

  • fixed header
  • flexible main content
  • fixed footer

The intended behavior is simple: the page itself stays still, and the middle region scrolls.

In Flexbox, the defensive version looks like this:

.app {
  min-height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.app__main {
  flex: 1 1 0;
  min-height: 0;
}

.app__scroll {
  block-size: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
}

In Grid, the same idea becomes:

.app {
  min-height: 100dvh;
  overflow: hidden;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
}

.app__main {
  min-height: 0;
}

.app__scroll {
  block-size: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
}

There are two important ideas here:

  • the middle region must actually be allowed to become smaller than its content, which is why min-height: 0 matters
  • using an inner scroll wrapper is often more defensive than making the flexible layout item itself do every job at once

If you skip those rules, the browser may let the content expand the region instead of creating the scroll behavior you expected.

3. justify-content: flex-end can make overflow unreachable

This trap is easy to miss in horizontally scrollable Flexbox rows.

You might write:

.rail {
  display: flex;
  justify-content: flex-end;
  overflow-x: auto;
  gap: 0.75rem;
}

That looks reasonable if the goal is "keep the cards on the right."

The problem is that justify-content: flex-end shifts the whole group toward the end side. Once content overflows, part of it may end up beyond the start side of the scroller in a way the user cannot fully reach.

The safer pattern is to keep normal flow and use an auto margin on the first item instead:

.rail {
  display: flex;
  overflow-x: auto;
  gap: 0.75rem;
}

.rail > :first-child {
  margin-inline-start: auto;
}

That produces the same "push this group to the end" effect without turning the scroll behavior into a trap.

For column-direction containers, the twin move is margin-block-start: auto.

This is a good general Flexbox habit:

use auto margins when one item or cluster needs to peel away from the rest, especially if the same container may also need to scroll.

If that pattern feels familiar, it is the same "let one item claim leftover distance" idea used throughout Flexbox Fundamentals: Axes, Alignment, and the Real Mental Model Behind One-Dimensional Layout.

4. Center alignment can also hide overflow at the start edge

Another subtle version of the same problem appears when a flex container uses cross-axis centering while content is allowed to overflow.

For example:

.stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow-x: auto;
}

This looks fine until one child becomes wider than the container. Then the item overflows on both sides, but the start-side overflow may be effectively lost to the user because the centered positioning keeps part of the content outside the reachable scroll area.

Long term, safe alignment is the cleaner model:

.stack {
  align-items: safe center;
}

But if your browser matrix cannot rely on that behavior, a more portable fallback is often to center the items with auto margins instead of cross-axis centering:

.stack > * {
  margin-inline: auto;
}

That keeps the "center when it fits" feel without forcing start-side overflow into an inaccessible position.

5. Default stretching is why controls suddenly look deformed

Many developers are surprised when images, buttons, or icon wrappers become taller than expected inside Flexbox or Grid. The reason is usually not a bug. It is the default alignment behavior.

In Flexbox, align-items defaults to stretch.

In Grid, items also stretch by default in their alignment axis unless told otherwise.

That means a layout like this can distort the smaller pieces:

.card {
  display: flex;
  gap: 0.75rem;
}

or:

.card {
  display: grid;
  grid-template-columns: min-content minmax(0, 1fr) min-content;
  gap: 0.75rem;
}

If one text block becomes much taller, its siblings may stretch to match the track or line size.

The fix is usually simple:

.card--flex {
  align-items: flex-start;
}

.card--grid {
  align-items: start;
}

Or, if only one child needs protection:

.card__icon,
.card__action {
  align-self: start;
}

This is one of the easiest defensive rules to adopt:

if your UI contains controls, media, badges, or icons that should keep their own shape, do not leave cross-axis stretching to chance.

If you want the wider alignment model behind these defaults, compare the Flexbox view in Flexbox Fundamentals: Axes, Alignment, and the Real Mental Model Behind One-Dimensional Layout with the Grid-specific version in CSS Grid Placement, Overlap, and Alignment: How Items Actually Land Where You Expect.

6. Flexbox can also squash UI that looks fixed

Stretching is one problem. Shrink pressure is another.

Suppose a media object or compact row contains:

  • thumbnail
  • text block
  • action button

If the text becomes long, Flexbox may squeeze the other items unless you make the contract explicit.

A more defensive version looks like this:

.item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.item__media,
.item__action {
  flex-shrink: 0;
}

.item__text {
  flex: 1 1 0;
  min-width: 0;
}

That combination matters:

  • flex-shrink: 0 protects the parts that should keep a stable visual size
  • min-width: 0 allows the flexible text region to actually give up space

If the text should truncate rather than wrap, combine it with:

.item__text {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

This is also why long labels often seem to "steal" space from icons or buttons. Flexbox is negotiating negative space across the line. If you do not protect the fixed-looking items, they are still allowed to participate in the squeeze.

7. A compact mental model helps more than memorizing fixes

When a Flexbox or Grid layout behaves strangely, ask which layer is actually causing the problem:

  • sizing layer: is a flexible region allowed to shrink?
  • overflow layer: is the container allowed to scroll in the direction the overflow is happening?
  • alignment layer: is centering or end alignment pushing overflow into an unreachable place?
  • stretch layer: are items being stretched because the default alignment is still active?
  • shrink layer: are small UI pieces being squeezed because nothing told them to stay fixed?

That diagnostic sequence is much faster than toggling random CSS properties.

8. A defensive checklist you can reuse

Before shipping a flexible layout, check:

  1. Does the scrollable region have a real height contract, such as min-height: 0 on the flexible parent and overflow: auto on an inner wrapper?
  2. Am I using justify-content: flex-end on a container that may also need to scroll?
  3. Could cross-axis centering push overflow beyond the start edge?
  4. Should align-items stay at stretch, or do these children need start, center, or item-level align-self?
  5. Do icons, thumbnails, or action buttons need flex-shrink: 0?
  6. Does the flexible text or content region need min-width: 0 or truncation styles?

That checklist catches a lot of "looks fine until real content arrives" bugs.

9. The takeaway

The most frustrating Flexbox and Grid bugs are often not advanced at all. They come from a few defaults that are easy to overlook:

  • flexible items do not always shrink the way you assume
  • scroll containers need an explicit size contract
  • end or center alignment can make overflow unreachable
  • stretch is often the wrong default for real UI components
  • fixed-looking items will still shrink unless you protect them

The fixes are usually tiny:

  • min-height: 0
  • an inner scroll wrapper
  • margin-inline-start: auto instead of justify-content: flex-end
  • align-items: flex-start or align-items: start
  • flex-shrink: 0
  • min-width: 0

Once these habits become automatic, a lot of "weird browser bugs" stop being weird. They become predictable layout rules you can work with on purpose.

Reviewed by

DevDepth Editor

Editor and frontend engineering writer

DevDepth publishes practical guides on React, Next.js, TypeScript, frontend architecture, browser APIs, and performance optimization.

Each article should be reviewed for technical accuracy, code clarity, metadata quality, and internal-link fit before it goes live.

Last editorial review: 2026-03-17

Contact the editor