Explain grid-template-columns, fr units, and minmax()
Advertisement
🧩 Scenario
Architecture Walkthrough
grid-template-columns Declares the Track List
grid-template-columns takes a space-separated list of track sizes, and the number of values is the number of columns. Each value can be a length, a percentage, an fr unit, auto, an intrinsic keyword such as min-content, max-content, or fit-content(), or a minmax() function. repeat() is available to avoid writing repetitive lists, and line names can be inserted in square brackets between tracks.
grid-template-rows works identically on the block axis. Anything not covered by the explicit track list is handled by the implicit grid.
The fr Unit Divides Leftover Space
fr stands for fraction, and the key point is which quantity it fractions. It divides the free space remaining after fixed tracks, content-based tracks, and all gaps have been accounted for. It is not a percentage of the container.
Work through grid-template-columns: 200px 1fr 2fr with gap: 20px in a 1000px container. Two gaps consume 40px. The fixed track takes 200px. That leaves 760px of free space, divided in a 1:2 ratio, giving 253.33px and 506.67px. The tracks are therefore 200px, 253.33px, and 506.67px.
This is exactly why fr is preferable to percentages. repeat(3, 33.33%) with a 20px gap overflows the container, because the percentages sum to the full width and the gaps are added on top. repeat(3, 1fr) cannot overflow that way, because gaps are subtracted before the free space is divided.
1fr Is Not What Most People Think
Here is the detail that matters most in practice: 1fr is shorthand for minmax(auto, 1fr). It carries an automatic minimum, which means the track will not shrink below its content's minimum size.
For most content that is invisible. But put a long unbreakable string, a wide image, or a white-space: nowrap element in a cell and that auto minimum floors the track at the content width. The track refuses to shrink, the other fr tracks lose their share, and the grid overflows its container. Every declaration looks correct and the layout is broken.
The fix is minmax(0, 1fr), which replaces the auto minimum with zero and lets the track shrink freely. In practice, repeat(3, minmax(0, 1fr)) is the safer default for any grid whose content you do not fully control, and repeat(auto-fit, minmax(0, 1fr))-style patterns appear throughout production stylesheets for exactly this reason. Grid's minmax(0, 1fr) is the direct counterpart of Flexbox's min-width: 0.
minmax() in General
minmax(min, max) sets a floor and a ceiling for a track. The minimum accepts lengths, percentages, and the intrinsic keywords but not fr; the maximum accepts all of those plus fr.
The combinations worth knowing:
minmax(200px, 1fr)is the responsive workhorse: never narrower than 200px, otherwise share the free space. Paired withauto-fitit produces a responsive grid with no media queries.minmax(0, 1fr)is the overflow-proof equal track.minmax(min-content, max-content)is effectively whatautodoes for a track.minmax(auto, 300px)grows with content up to a ceiling, which is whatfit-content(300px)expresses more concisely.
auto Versus fr
An auto track sizes to its content, and if there is leftover free space it does receive a share, but only when no fr tracks are present to claim it. As soon as any track uses fr, the fr tracks absorb all the free space and auto tracks settle at their content size.
That distinction is what makes auto 1fr auto the right pattern for a row with content-sized ends and a flexible middle: the outer tracks take exactly what their content needs, and the middle takes everything left over.
Key Code Explained
/* Mixed track list: 1000px container, gap: 20px */
.layout {
display: grid;
grid-template-columns: 200px 1fr 2fr;
gap: 20px;
}
/* gaps = 2 x 20 = 40px
fixed = 200px
free = 1000 - 40 - 200 = 760px
1fr = 760 / 3 x 1 = 253.33px
2fr = 760 / 3 x 2 = 506.67px */
/* Why fr beats percentages: this OVERFLOWS by 40px */
.percent-grid {
display: grid;
grid-template-columns: repeat(3, 33.33%);
gap: 20px; /* added ON TOP of 100% of width */
}
/* This cannot: gaps are subtracted before fr divides the remainder */
.fr-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
/* THE OVERFLOW TRAP: 1fr means minmax(auto, 1fr) */
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
/* A cell containing a long unbreakable URL floors its track at the
content width, so the track will not shrink and the grid overflows. */
/* THE FIX: replace the auto minimum with zero */
.cards-safe {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* This is Grid's equivalent of min-width: 0 on a flex item. */
/* minmax() combinations worth knowing */
.responsive {
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.capped {
grid-template-columns: minmax(auto, 300px) 1fr;
}
.same-as-capped {
grid-template-columns: fit-content(300px) 1fr;
}
/* auto vs fr: auto tracks take content size once any fr track exists */
.toolbar-row {
display: grid;
grid-template-columns: auto 1fr auto; /* icon | flexible label | actions */
align-items: center;
gap: 12px;
}
/* Intrinsic keywords as track sizes */
.intrinsic {
grid-template-columns: max-content min-content 1fr;
}
/* Named lines and repeat() together */
.named {
grid-template-columns: [full-start] 1fr [content-start] minmax(0, 960px) [content-end] 1fr [full-end];
}
The .cards versus .cards-safe pair is the single most valuable thing here. The two declarations differ by one function call, and the difference decides whether a card grid survives a long URL, a code snippet, or a nowrap badge in one of its cells. Knowing that 1fr expands to minmax(auto, 1fr) is what makes that predictable instead of mysterious.
The .percent-grid versus .fr-grid comparison is the second one to keep. It is the concrete reason fr exists rather than being sugar over percentages: fr is computed after gaps are removed, so gap and track sizing compose without calc().
Tradeoffs
| Track value | Sizes to | Absorbs free space | Overflow risk |
|---|---|---|---|
200px | Exactly 200px | No | Only if the sum exceeds the container |
33.33% | Percentage of container | No | Yes, gaps add on top |
1fr | minmax(auto, 1fr) | Yes | Yes, content sets a floor |
minmax(0, 1fr) | Free space share, no floor | Yes | No |
auto | Content, plus free space only if no fr exists | Conditionally | Low |
min-content | Narrowest without internal overflow | No | No |
max-content | Widest, no wrapping | No | Yes |
fit-content(300px) | Content up to 300px | No | No |
What Interviewers Actually Check
- Whether you can compute track widths from a mixed list including gaps
- Whether you explain
fras dividing leftover space rather than as a percentage - Whether you know
1frexpands tominmax(auto, 1fr)and what that implies - Whether you reach for
minmax(0, 1fr)when content is untrusted - Whether you can distinguish an
autotrack from anfrtrack when free space exists
Follow-Up Questions
- What happens if the
frtracks' total minimum content size exceeds the container even withminmax(0, 1fr)? - How does
fit-content()differ fromminmax(auto, <length>)in edge cases? - Can
frbe used as the minimum argument ofminmax(), and why not? - How do
min-contentandmax-contenttracks behave differently for a cell containing a long paragraph? - What does
grid-template-columns: subgriddo, and what problem does it solve thatminmax()cannot?
Common Candidate Mistakes
- Describing
1frin a three-column grid as equivalent to33.33%, which is only true when there is no gap and no fixed or content-sized track in the list - Not knowing
1frcarries anautominimum, so a long unbreakable string in one cell floors that track and overflows the whole grid - Assuming
gapis added on top offrtracks and can cause overflow, when gaps are subtracted from the container before free space is divided - Reaching for percentage tracks and then adding
calc()to compensate for gaps, work thatfrdoes automatically - Treating
autoand1fras interchangeable, whenautoyields the free space to anyfrtrack present and settles at its content size
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you compute track widths for a mixed track list including gaps?
- Can you explain what
frdivides and why it is not a percentage? - Can you explain what
1frexpands to and why that causes overflow with wide content? - Can you write
minmax()using intrinsic keywords as either argument? - Can you explain the difference between an
autotrack and a1frtrack when free space exists?
Summary
grid-template-columns declares a track list, where each value can be a length, a percentage, an fr unit, auto, an intrinsic keyword, or a minmax() function, and repeat() shortens repetitive lists. The number of values determines the number of explicit columns.
The fr unit divides the free space remaining after fixed tracks, content-sized tracks, and all gaps are accounted for. That is why 200px 1fr 2fr with a 20px gap in a 1000px container yields 200px, 253.33px, and 506.67px, and why fr is safer than percentages: repeat(3, 33.33%) plus a gap overflows because the percentages already claim the full width, while fr cannot overflow for that reason.
The detail that causes real bugs is that 1fr is shorthand for minmax(auto, 1fr). The auto minimum floors the track at its content's minimum size, so a long unbreakable string or a wide image in one cell prevents that track from shrinking and pushes the grid past its container. minmax(0, 1fr) removes the floor and is the Grid counterpart to min-width: 0 on a flex item. Beyond that, minmax(200px, 1fr) is the responsive workhorse that pairs with auto-fit, fit-content() caps a content-sized track, and auto tracks take their content size as soon as any fr track is present to claim the leftover space.
Why does 1fr overflow when the content is wide?
Because 1fr means minmax(auto, 1fr), and the auto minimum floors the track at its content size. minmax(0, 1fr) removes that floor.
Does fr account for gap?
Yes. Gaps are subtracted from the container size before the free space is divided among fr tracks, so gaps never cause overflow on their own.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement