minmax() and fr Units in Grid: The Edge Cases That Cause Overflow
AI generated
{ }
@
CSS · Grid Layout · Layout Debugging
minmax() and fr Units
The edge cases that cause overflow in grid

1fr sounds like a simple, flexible share, but it does not behave the way it looks. Grid items carry an implicit minimum size that can be larger than their fr share, and that exact mechanism produces the overflow bug that makes minmax(0, 1fr) the real default, not just an optimization for edge cases.

13 min read CSS Grid Level 1 minmax() · fr · auto-fit · auto-fill

1. fr and minmax(): the base formula for responsive grid columns

The fr unit describes a share of the available free space inside a grid container, after all fixed-size tracks and all content have been subtracted at their minimum size. grid-template-columns: 1fr 1fr 1fr therefore creates three equally wide columns that split the entire available space between them, as long as nothing else interferes. That simplicity is exactly why fr is the most popular unit for responsive layouts.

minmax() defines a lower and an upper bound for a track and is usually combined with fr, for example minmax(200px, 1fr): the column can never shrink below 200 pixels but grows flexibly beyond that relative to other fr tracks. In practice, plain 1fr on its own holds up less often than expected, because it ignores a factor known in the specification as the automatic minimum size, and that factor regularly causes overflow.

2. Why 1fr alone is not enough: the implicit minimum size of grid items

Every grid item has a default minimum size of min-width: auto, respectively min-height: auto, even when that property was never set explicitly. This automatic minimum size follows the item's content: an image never shrinks below its intrinsic width, an unbreakable text string like a long URL or a code block never gets narrower than its longest unbroken run of characters, no matter how small the assigned fr column was actually meant to be.

The problem shows up once this content-based minimum size exceeds the space 1fr would actually allocate to the item. In that case the minimum size wins, the item stays wider than its column, and the grid container overflows its own available space. This exact mechanism, not a browser bug, is the most common cause of horizontal overflow in grid layouts with long or unpredictable content.

3. minmax(0, 1fr): how it solves the problem and prevents overflow

The fix is surprisingly simple: minmax(0, 1fr) explicitly sets the track's minimum size to 0, completely overriding the automatic, content-based minimum size. The item can now actually shrink down to its assigned fr share, even if its content would technically want more room. That does not automatically resolve overflow inside the cell, but at least the grid itself stays within its container width, and the item's remaining overflow can be handled in detail with overflow: hidden, overflow-wrap, or text-overflow.

Because this behavior matters in virtually every grid with variable or user-generated content, minmax(0, 1fr) has become the real default formula in practice, not a special case for exceptions. Anyone using plain 1fr without minmax risks a layout that breaks past the visible area with every longer word, every long URL, or every embedded code block, and that usually surfaces late in development, once real content replaces placeholder text.


/* Problematic: 1fr alone is not enough.
   Long, unbreakable content blows out the column. */
.row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 1rem;
}

/* Fix: minmax(0, 1fr) allows real shrinking
   down to the assigned share. */
.row--fixed {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
}

.row__cell {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

4. fr and content size: the interaction between intrinsic size and distribution

The fr distribution itself accounts for intrinsic content sizes in a two-step process: the browser first assigns every track its minimum size, then distributes the remaining free space proportionally according to the fr values. If one column contains wider content than another even though both share the same fr value, that column can still end up wider, because its minimum size was higher and less of the remaining space was left to distribute to the other columns.

That produces an effect that looks contradictory at first glance: two columns with an identical 1fr value do not necessarily end up equally wide if their content differs in size and minmax(0, ...) is missing. Only with minmax(0, 1fr) does the minimum size for both columns get set to the same starting value of zero, which makes the pure fr distribution behave as expected again, producing equally wide columns regardless of content.

5. A common bug: horizontal overflow from code or tables inside grid columns

A particularly common real-world case is a card grid where individual cards contain code blocks, long file paths, or technical IDs. Without minmax(0, 1fr), the unbreakable code block pushes its card past the assigned fr width, which either widens the entire grid row or, depending on the container, causes horizontal scrolling across the whole page, often only noticed once a user views a real record with long values.

The second common case involves embedded tables: a table inside a grid item often carries its own minimum width, driven by its content, and passes that width up to the grid item. Combining minmax(0, 1fr) on the grid item with overflow-x: auto on an inner wrapper around the table reliably fixes both symptoms, because the grid itself can shrink while the table scrolls independently inside its wrapper.


.card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem;
}

.card {
  min-width: 0;
}

/* Inner wrapper catches a wide table without
   it blowing out the grid itself */
.card__table-wrap {
  overflow-x: auto;
}

6. auto-fit vs. auto-fill with minmax(): the difference with empty columns

repeat(auto-fit, minmax(200px, 1fr)) and repeat(auto-fill, minmax(200px, 1fr)) first compute identically how many tracks fit into the container by width. The difference only shows up once fewer items exist than tracks would fit: auto-fit collapses the surplus, empty tracks down to a width of 0 and redistributes the freed space to the existing items, so they fill the entire row.

auto-fill, by contrast, keeps the width of the empty tracks even when no item remains to fill them. The result: with few items, auto-fill leaves visible blank space at the end of the row, while auto-fit stretches the existing items wider. For card grids with an unknown, variable number of elements, auto-fit is almost always the right choice, while layouts with a fixed, expected column count, such as a calendar grid, are better served by auto-fill.


/* auto-fit: empty tracks collapse, existing
   items stretch wider to fill the row */
.cards--fit {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1rem;
}

/* auto-fill: empty tracks stay as visible
   blank space, items keep their width */
.cards--fill {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
}

7. Nested grids: how the minmax problem propagates

Placing another, nested grid inside a grid item inherits the exact same problem on an additional level: the inner grid's own columns also carry a default automatic minimum size, driven by their own content. If minmax(0, 1fr) is missing on the outer level, oversized content in the inner grid can push its way outward through both levels and blow out the outer grid container, even though the outer container was configured correctly by itself.

In practice that means minmax(0, 1fr) needs to be set at every nesting level where an fr share gets assigned, not just the outermost one. A reliable pattern is applying min-width: 0 (or min-block-size: 0 for the block direction) globally to all direct grid item children, so nested grids can consistently shrink regardless of their depth instead of hitting the same bug at every new level.

8. A practical recipe: a responsive card grid with images and no overflow

A typical responsive card grid combines repeat(auto-fit, minmax(240px, 1fr)) for the column width with minmax(0, 1fr), respectively min-width: 0, on each card itself, so neither the outer track nor the inner card block gets blown out by an image, heading, or meta text. Images also get width: 100% and height: auto so they adapt to their cell width instead of passing their intrinsic width along as a minimum size.

For headings with long, unbroken words, such as technical terms or URLs, overflow-wrap: break-word complements the minmax fix, because minmax itself only controls the track, not text wrapping inside the track. Only the combination of both techniques guarantees that a card grid stays stable across every realistic content length, from short test headings to long, real product names.


.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.5rem;
}

.card {
  min-width: 0;
  display: grid;
  grid-template-rows: auto 1fr;
}

.card img {
  width: 100%;
  height: auto;
  display: block;
}

.card h3 {
  overflow-wrap: break-word;
}

9. Debugging strategy: systematically finding overflow causes in a grid

The fastest way to identify an overflow cause in a grid is to add outline: 1px solid red step by step in DevTools, on the grid container and all direct children, to visually reveal which element extends past its cell. That very often immediately shows a single item, usually holding long text or embedded code, as the actual cause, while the remaining columns shrink correctly.

As a second step it pays to explicitly set min-width to 0 in DevTools and watch whether the overflow issue disappears. If it does, that confirms the implicit minimum size as the cause, and minmax(0, 1fr), respectively a direct min-width: 0 on the item, is the correct permanent fix. If overflow persists, the cause usually lies elsewhere, for example a fixed width value or an oversized image lacking max-width: 100%.

Pattern Overflow risk Cause Recommendation
1fr without minmax High with long content Implicit minimum size min-width: auto Always use minmax(0, 1fr)
minmax(0, 1fr) Low Minimum size explicitly set to 0 Default for responsive card grids
minmax(200px, 1fr) Medium with very long words Minimum width above 0 Add overflow-wrap: break-word as well
auto-fit Low Empty tracks collapse to 0 For a variable item count
auto-fill Low, but empty columns stay visible Empty tracks keep their width Prefer for an even grid width

Mironsoft

Modern CSS, layout architecture and rendering performance

CSS that stays maintainable instead of breaking with every change?

We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.

CSS Audit

Systematically uncovering specificity issues, cascade conflicts and unused selectors.

Architecture Refactoring

Introducing cascade layers, custom properties and design tokens cleanly.

Performance Tuning

Fixing layout thrashing, expensive selectors and rendering bottlenecks.

10. Summary

minmax() and fr Units: The Essentials at a Glance

Core problem

Grid items carry a default implicit minimum size of min-width: auto that can exceed the assigned fr share, which is what causes overflow.

The fix

minmax(0, 1fr) explicitly sets the minimum size to 0 and lets the item shrink to its fr share instead of blowing out the container.

auto-fit vs. auto-fill

auto-fit collapses empty tracks to zero width, auto-fill keeps their width even when no items remain to fill them.

Debugging

Trace horizontal overflow in grid columns back to long, unbreakable content like code or URLs and apply minmax(0, 1fr) plus overflow-wrap specifically.

11. FAQ: minmax() and fr Units: The Essentials at a Glance

1Why is grid-template-columns: 1fr 1fr not always enough?
Because every grid item has a default implicit minimum size of min-width: auto, driven by its content. If the content is wider than the assigned fr share, the minimum size wins and the grid overflows.
2What does minmax(0, 1fr) actually do?
It explicitly sets the track's minimum size to 0, overriding the automatic, content-based minimum size, so the item can actually shrink down to its fr share.
3Do I need minmax(0, 1fr) on every column?
Yes, on every column that could potentially hold variable or long content. For columns guaranteed to have short, fixed content it is optional, though it never hurts there either.
4What is the difference between auto-fit and auto-fill?
auto-fit collapses surplus empty tracks to zero width and redistributes the space to existing items. auto-fill keeps the width of empty tracks, which can leave visible blank space.
5Does minmax(0, 1fr) also fix overflow inside a cell?
No, it only prevents the cell itself from growing wider than its fr share. Text inside the cell still needs overflow-wrap, text-overflow, or overflow-x as well.
6Why are two columns with an identical 1fr value sometimes different widths?
Because their respective content-based minimum sizes can differ. Only minmax(0, 1fr) sets both minimum sizes to the same starting value and produces true equal distribution.
7Does the minmax problem propagate through nested grids?
Yes. Every nesting level that assigns an fr share needs its own minmax(0, 1fr), otherwise oversized content from an inner grid can blow out the outer container.
8How do I find the cause of a grid overflow bug?
Add outline borders to the container and its children to identify the overflowing element, then temporarily set min-width to 0. If the overflow disappears, the implicit minimum size was the cause.
9Do I need minmax(200px, 1fr) or is minmax(0, 1fr) enough?
That depends on the desired layout. minmax(200px, 1fr) guarantees a minimum width per column, minmax(0, 1fr) only guarantees the column never grows beyond its fr share.
10Does the same logic apply to flexbox?
Yes, flex items carry the same implicit minimum size of min-width: auto. There the fix is usually min-width: 0 directly on the flex item instead of minmax.