CSS Grid repeat() and minmax() Explained
repeat() and minmax() let CSS Grid build responsive layouts without media queries. How the two functions combine, and common patterns.
repeat() and minmax() are CSS Grid functions that let a track listing describe a range of possible layouts instead of one fixed one — repeat() avoids typing out the same column definition over and over, and minmax() gives a track a floor and a ceiling instead of a single size. Combined, they’re the standard way to build grids that reflow to fit available space without writing a single media query.
What repeat() does on its own
A grid’s columns are defined with grid-template-columns, and without repeat() a three-column grid means writing the same value three times:
.grid {
grid-template-columns: 1fr 1fr 1fr;
}
repeat() collapses that into a count and a pattern:
.grid {
grid-template-columns: repeat(3, 1fr);
}
The pattern can be more than one track — repeat(2, 100px 1fr) produces four columns, alternating a fixed 100px column with a flexible one, twice. This is useful for repeating layouts like label/value pairs or icon/text rows, but it’s still a fixed count. The layout doesn’t adapt to the container’s width on its own; you’d still need a media query to change the count at different breakpoints. That’s where minmax() and the auto-fill/auto-fit keywords come in.
What minmax() adds
minmax(min, max) defines a track’s acceptable size range instead of a single value. The track sizes to fill available space but never shrinks below min or grows past max. A common use is preventing content from becoming unreadably narrow:
.grid {
grid-template-columns: repeat(3, minmax(200px, 1fr));
}
Each column is at least 200px wide and shares the remaining space equally via 1fr — but only once the minimum is satisfied. If the container isn’t wide enough to fit three 200px columns, the columns overflow rather than shrinking further, which is usually the signal you actually want a different column count at that width. That’s the gap auto-fit and auto-fill close.
Combining both: responsive grids without breakpoints
The pattern that shows up constantly in real stylesheets replaces a literal repeat count with auto-fill or auto-fit:
.grid {
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
Instead of a fixed number of columns, the browser computes how many minmax(220px, 1fr) tracks fit in the container and lays out that many — recalculating on every resize. Shrink the viewport and columns drop out and reflow; widen it and more columns appear. No media query specifies the breakpoints; the breakpoints emerge from the content’s own minimum size.
auto-fill vs auto-fit
Both count how many tracks of at least the minimum size fit in the row, but they differ in what happens with the space left over when there are fewer grid items than columns that could fit:
auto-fillkeeps the empty tracks in the grid, collapsed to zero width but still reserved as tracks. If the extra tracks would otherwise absorb the1frshare, items can end up narrower than expected because that share is spread across placeholder columns too.auto-fitcollapses empty tracks to nothing and lets the existing items expand to fill the freed space, since1fris distributed only among the tracks that actually contain content.
In practice, auto-fit is what most “responsive card grid” layouts want — items stretch to fill a mostly-empty row rather than leaving visible gaps. auto-fill is closer to right when you deliberately want to reserve consistent-width slots, like a fixed-looking grid of thumbnails that shouldn’t grow past their natural size even when there’s room.
repeat() and minmax() vs media-query grids
| repeat()/minmax()/auto-fit | Media-query grid | |
|---|---|---|
| Breakpoints | Emerge from content’s min size | Manually defined pixel widths |
| Column count | Computed per render | Fixed per breakpoint |
| Maintenance | One rule covers all widths | New rule needed per breakpoint added |
| Precision | Approximate, content-driven | Exact, designer-controlled |
| Best for | Card grids, galleries, tag lists | Layouts needing exact column counts at specific sizes |
Media queries still win when a design calls for an exact column count at an exact width — a two-column layout that must become three columns precisely at 900px, for instance. auto-fit/auto-fill grids are approximate by nature: the column count is whatever fits, not a number you chose. For most card and gallery layouts that approximation is the point, but layouts with strict visual rhythm still need explicit breakpoints, sometimes defined with clamp() for the sizes in between.
Where this fits with other layout tools
repeat() and minmax() are grid-specific — they don’t have equivalents in flexbox, which distributes space along a single axis rather than sizing independent tracks. For layouts that need named regions rather than an even repeating pattern, grid-template-areas is the better fit. And for grids nested inside grids that need to share the parent’s column tracks, subgrid solves a problem repeat()/minmax() can’t — it doesn’t make nested tracks align with the outer grid on its own.
minmax() also composes with container queries rather than competing with them: auto-fit/auto-fill responds to the grid’s own available width, while a container query lets a card inside that grid change its internal layout based on the width it actually got. Combining the two is common in component-driven CSS, where the outer grid handles column count and each card handles its own internal breakpoints.
The takeaway
repeat() removes repetition from a track listing; minmax() gives a track a size range instead of a fixed value. Paired with auto-fit or auto-fill, repeat(auto-fit, minmax(min, 1fr)) computes column count from content size on every resize, replacing a stack of media queries with one rule. Reach for it on card grids, galleries, and tag lists where the exact column count doesn’t matter as much as items staying above a readable minimum width — and fall back to explicit breakpoints when a design needs an exact column count at an exact size.
Tagged
Keep reading
Takina · · 4 min read CSS object-fit and object-position, Explained
object-fit controls how an image or video is cropped inside its box, and object-position controls which part of it stays visible. How they work together.
Takina · · 5 min read CSS inherit, initial, unset & revert Explained
CSS's four global keywords control where a property's value comes from. How inherit, initial, unset, and revert differ, with a comparison table.
Takina · · 4 min read Dynamic Viewport Units: dvh, svh, and lvh Explained
dvh, svh, and lvh fix the classic mobile vh bug where browser toolbars cut off full-height layouts. Here's what each unit measures and when to use it.