ESC

Type to search the knowledge base.

Auto-fit vs Auto-fill

auto-fit collapses empty tracks; auto-fill keeps them. Use both with minmax for responsive card grids without media-query soup.

intermediate3 min read
  • css
  • auto-fit
  • grid
  • minmax

The classic responsive card grid is one line of Grid when you understand auto-fill vs auto-fit inside repeat() with minmax(). Interviewers love this because it separates “I memorized a snippet” from “I know what empty tracks do.”

Docs: MDN repeat(), MDN minmax(), Grid auto-placement.

The one-liner people paste

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

That says: create as many columns as fit, each at least 16rem, share leftover space equally (1fr), and collapse empty tracks so filled columns expand.

Swap auto-fit → auto-fill and empty tracks remain as reserved columns — items won’t stretch into that ghost space the same way.

Mental model

Both keywords tell the browser: count how many tracks of this size fit in the container, not “always N columns.”

Keyword Empty tracks Visual effect with few items
auto-fill Kept Items stay track-sized; trailing empty columns may leave free space on the end
auto-fit Collapsed (size → 0, gutters adjusted) Items stretch to fill the row
/* Fill: keep ghost columns */
.gallery-fill {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0.75rem;
}

/* Fit: stretch real items across the row */
.gallery-fit {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0.75rem;
}

With many items packing every track, auto-fit and auto-fill often look identical. The difference shows when the last row (or the only row) is sparse.

Why minmax is required for the pattern

/* Bad mental model: fixed track size only */
repeat(auto-fill, 200px); /* works, but tracks never grow */

/* Product pattern: floor + grow */
repeat(auto-fit, minmax(200px, 1fr));
  • Min (200px / 16rem): don’t crush cards below readable width; wrap to fewer columns instead.
  • Max (1fr): leftover free space is shared so columns grow evenly.

Use rem (or ch) for the floor when card content is typographic; use px when the design is pixel-locked to images.

Worked example: product grid

<ul class="product-grid">
  <li class="card">…</li>
  <li class="card">…</li>
  <li class="card">…</li>
</ul>
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 1.25rem;
  list-style: none;
  padding: 0;
}

.card {
  min-width: 0; /* allow grid item to shrink; avoid overflow from long words */
}

min(100%, 18rem) prevents overflow when the container itself is narrower than 18rem (nested grids, narrow sidebars). Without it, minmax(18rem, 1fr) can force horizontal scroll.

Choosing intentionally

Prefer auto-fit when:

  • Sparse rows should look full (marketing feature rows, 2–3 cards)
  • You want columns to expand into unused space

Prefer auto-fill when:

  • You want consistent card widths even with one item
  • Empty slots matter for alignment with a denser row above
  • You’re placing items into a “slot grid” aesthetic

Footguns

  1. Confusing fill/fit when the grid is full — demo with 1–2 items in DevTools.
  2. Huge minmax minimum — one column forever on tablets.
  3. Forgetting min-width: 0 on grid items with media or preformatted text.
  4. Mixing with fixed column counts without a clear breakpoint strategy — pick one system.
  5. Gap math — tracks fit after gaps; unexpected wrap is often gap + padding, not fill vs fit.

Interview out-loud

“auto-fill and auto-fit both pack as many tracks as fit using the track list. With minmax(min, 1fr), tracks share free space. auto-fit collapses empty tracks so items stretch; auto-fill keeps empty tracks. They look the same when every track has an item. I use min(100%, minSize) in the minmax floor to avoid overflow in narrow containers.”

Further reading

Related guides