Flexible · Chapter 6 of 7 · 1 unit
The fr Unit
Not a length at all: a share of whatever is left.
fr is the odd one out. Every other unit in this guide resolves to a length: pixels, eventually. fr doesn’t. It’s a flexible length that only means anything inside CSS Grid, and it represents a share of the leftover space rather than a fixed measurement.Its spec type is <flex>, and fr is the only unit in it. Angles, times and resolutions have their own types too — but they measure quantities. <flex> exists to express a proportion, which is why it can’t mix with lengths.
1fr = one fraction of the free space in the grid container
The word that matters is leftover. fr is computed last, after fixed tracks, gaps, and content-based minimums have already taken their space. Whatever remains is divided among the fr tracks in proportion to their values.
fr distributes leftover space—Every fr track shares the space left after fixed tracks, gaps, and content minimums are removed. Try 200px 1fr — the1fr track takes everything except the fixed 200px.
fr
Consider three equal columns:
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
The container width is split into three equal shares — each track is one-third.
Change the proportions and the shares re-weight:
grid-template-columns: 1fr 2fr 1fr;
Now there are 1 + 2 + 1 = 4 shares. The middle track gets 2/4 (half the width); the outer tracks get 1/4 each.
Mix fixed and flexible tracks, and the fixed ones are removed first:
grid-template-columns: 200px 1fr;
The fixed 200px (plus any gap) is subtracted, and the single 1fr track takes all the remaining space. This is the canonical sidebar-plus-content layout.
- Proportional column/row layouts:
1fr 2fr - Sidebar + content:
200px 1fror240px 1fr - Equal-width tracks:
repeat(3, 1fr) - Filling remaining space after fixed tracks are placed
- Combining with
minmax()andrepeat(auto-fit, …)for fluid grids
- Outside Grid —
fris invalid inwidth,flex-basis, etc. - When you actually want content-based sizing — use
autoormin-content - Assuming bare
1frcan shrink below its content — it has an implicitautominimum
The minmax(0, 1fr) trap
This is the single most common fr gotcha, and it causes mysterious overflow.
A bare 1fr is not minmax(0, 1fr). It’s effectively minmax(auto, 1fr). That auto minimum means the track will not shrink below the size of its content. So if a grid item contains something that can’t shrink (a long unbroken string, an image with intrinsic width, a white-space: nowrap element), the track refuses to go below that minimum, and the grid blows out of its container.
/* Can overflow: the fr track won't shrink below its content */
.grid {
grid-template-columns: 1fr 1fr;
}
The fix is to give the track a real zero minimum:
/* Allows the track to shrink below content size */
.grid {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
In the demo above, compare the 1fr 1fr 1fr template (long content forces the track wide) with minmax(0, 1fr) 1fr (the minmax track shrinks and ellipsizes).
fr can’t go inside calc()
fr is a flexible length, not a regular one, so it can’t be mixed with ordinary lengths in calc():
/* Invalid — fr is not allowed in calc() */
grid-template-columns: calc(1fr - 2em);
If you need “flexible track, minus some fixed space,” that’s what the track list itself is for: put the fixed amount in its own track or use minmax():
/* Do this instead */
grid-template-columns: 2em 1fr; /* fixed gutter + flexible track */
grid-template-columns: minmax(0, 1fr); /* flexible, allowed to shrink */
fr can sit alongside fixed lengths in the same track list, though: 250px repeat(12, 1fr) is perfectly valid; the flexible tracks just divide whatever the fixed 250px leaves behind.
fr is not flex-grow
fr and flex-grow feel similar (both distribute extra space proportionally), but they resolve differently, and conflating them leads to surprises.
flex-growdistributes only the extra space, and it does so on top of each item’sflex-basis(which defaults to the content size). Two items withflex-grow: 1but different content can end up different widths: each keeps its content-based starting size and only the leftover space is shared equally, so unequal content yields unequal totals.frdistributes the track space itself. Two1frtracks are equal regardless of content (assuming theautominimum doesn’t intervene), becausefrsizes the track, not the growth-beyond-basis.
/* Flexbox: items can end up unequal — growth adds to each item's content basis */
.flex { display: flex; }
.flex > * { flex-grow: 1; } /* basis stays auto → wider content keeps a wider item */
/* Grid: tracks are genuinely equal shares of the container */
.grid { display: grid; grid-template-columns: 1fr 1fr; }
(Note that flex: 1 is the equalizing case: its shorthand sets flex-basis: 0%, which is exactly what removes the content-based difference. Plain flex-grow: 1 is what exposes the inequality.)
A useful way to hold it:
flex-growanswers “how should the extra space be shared?”franswers “how should the whole space be divided into tracks?” That’s whyfrlives in Grid and not in Flexbox — Grid defines explicit tracks; Flexbox grows items.
Why fr only exists in Grid
fr needs a defined set of tracks to divide space among. Grid provides exactly that — an explicit track list in grid-template-columns / grid-template-rows. Flexbox has no track list; it has items that flex along a single axis, so it uses flex-grow/flex-shrink/flex-basis instead. That’s why fr is valid across Grid’s track-sizing properties — grid-template-columns/-rows, the grid-auto-columns/-rows that size implicit tracks, and the grid/grid-template shorthands — and nowhere outside Grid.
Compact reference
fr |
|
|---|---|
| Meaning | one share of the grid’s leftover free space |
| Computed | after fixed tracks, gaps, and content minimums are subtracted |
| Valid in | grid-template-columns / -rows, grid-auto-columns / -rows, and the grid / grid-template shorthands (incl. repeat() and minmax()) |
| Invalid in | width, flex-basis, gap, calc() — anything expecting a length |
| Pattern | Result |
|---|---|
1fr |
= minmax(auto, 1fr) — implicit auto minimum, can overflow |
minmax(0, 1fr) |
lets the track shrink below its content |
1fr 2fr 1fr |
shares 1 : 2 : 1 → 25% / 50% / 25% |
200px 1fr |
fixed track is removed first; fr takes the rest |
And the sibling concept: flex-grow distributes the extra space on top of each item’s flex-basis (content-aware), while fr divides the whole track space into shares (track-based).