Type something to search...
auto-fit vs. auto-fill in CSS Grid: What's the Difference?

auto-fit vs. auto-fill in CSS Grid: What's the Difference?

There's one line of CSS Grid that shows up in almost every modern card layout:

grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));

Or is it auto-fit? Both keywords appear in tutorials, both produce a responsive grid without media queries, and most of the time you can swap one for the other and see no difference at all. That's exactly why they're confusing. The difference only shows up in a specific situation, and when it does, it changes the layout noticeably.

This guide explains what repeat() does with each keyword, shows the exact case where they diverge, and gives you a simple way to decide which one you actually want.

The Building Blocks

Both keywords are used inside the repeat() function, usually combined with minmax(). Let's make sure those pieces are clear first.

repeat()

repeat() is a shorthand for writing the same track size multiple times:

/* These are equivalent */
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-columns: repeat(4, 1fr);

Instead of a fixed number, you can pass auto-fill or auto-fit as the first argument. That tells the browser to work out how many tracks fit in the container, based on the track size you give it.

minmax()

minmax(min, max) defines a track that can be no smaller than min and no larger than max:

grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));

Here each column is at least 200px wide and at most 1fr, meaning it can grow to share any leftover space equally.

When you use auto-fill or auto-fit, the browser needs a definite size to count how many columns fit. It uses the minimum of the minmax() (200px here) for that calculation, and then lets the tracks grow toward the maximum.

What Both Keywords Have in Common

With either keyword, the browser does the same first step:

  1. Take the width of the grid container.
  2. Figure out how many tracks of the minimum size (plus gaps) fit.
  3. Create that many column tracks.

Say the container is 1000px wide with a 20px gap and a 200px minimum. Four columns need 4 * 200 + 3 * 20 = 860px, which fits. Five columns need 5 * 200 + 4 * 20 = 1080px, which doesn't. So the browser creates four columns, with either keyword.

If you have eight items, they fill two rows of four, and the result is identical whether you wrote auto-fill or auto-fit. The columns stretch to share the extra 140px, and every card is 235px wide.

That's why most people never notice a difference. As long as you have enough items to fill at least one full row, the keywords behave the same.

Where They Diverge: Fewer Items Than Columns

The difference appears when there are fewer items than the number of tracks that fit.

Take the same 1000px container, but now with only two items.

<div class="grid">
  <article class="card">Card one</article>
  <article class="card">Card two</article>
</div>

With auto-fill

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 20px;
}

The browser still creates four columns, because four fit. Two of them hold the cards, and the other two stay empty but present. Each column is 235px wide, so the two cards occupy the left half of the container, and the right half is blank space where the empty tracks sit.

With auto-fit

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
}

The browser creates four columns too, but then does one extra step: it collapses any empty repeated tracks to zero width. The gaps next to them collapse as well. Now there are effectively two columns, and because they're 1fr, they stretch to fill the whole container. Each card ends up 490px wide.

That's the entire difference:

  • auto-fill keeps empty tracks. Items stay at the size they'd have in a full row.
  • auto-fit collapses empty tracks. Items stretch to fill the row.

A helpful way to remember it: auto-fill fills the row with columns, whether or not you have items for them. auto-fit fits the items you have to the row.

A Side-by-Side Demo

Here's a small page you can paste into a file to see both behaviors at once. Resize the window and try deleting cards.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>auto-fill vs auto-fit</title>
    <style>
      body {
        font-family: system-ui, sans-serif;
        padding: 2rem;
        background: #f8fafc;
      }

      .grid {
        display: grid;
        gap: 1rem;
        margin-block-end: 2rem;
        padding: 1rem;
        border: 2px dashed #94a3b8;
      }

      .grid--fill {
        grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
      }

      .grid--fit {
        grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      }

      .card {
        padding: 1.5rem;
        border-radius: 0.5rem;
        background: #6366f1;
        color: #ffffff;
      }
    </style>
  </head>
  <body>
    <h2>auto-fill</h2>
    <div class="grid grid--fill">
      <div class="card">One</div>
      <div class="card">Two</div>
    </div>

    <h2>auto-fit</h2>
    <div class="grid grid--fit">
      <div class="card">One</div>
      <div class="card">Two</div>
    </div>
  </body>
</html>

On a wide screen, the auto-fill cards stay narrow and leave a gap on the right. The auto-fit cards stretch across the whole container. Add cards until the first row is full, and the two grids become identical.

You can also see the empty tracks directly. Open DevTools, select the auto-fill grid, and turn on the grid overlay. The empty columns are outlined even though nothing is in them. On the auto-fit grid, the collapsed tracks show up as lines stacked on top of each other at the end.

The Difference Only Matters When Tracks Can Grow

The divergence depends on the maximum in minmax() being flexible. If the tracks have a fixed size, there's nothing to stretch into:

.grid {
  grid-template-columns: repeat(auto-fit, 200px);
}

With a fixed 200px track, auto-fit still collapses empty tracks, but the remaining columns stay 200px wide. Visually, you'd see the same thing as auto-fill: two 200px cards at the start of the row. The only visible difference arises with justify-content. With auto-fit, collapsed tracks take no space, so justify-content: center centers just the two real columns. With auto-fill, the empty tracks still take up room, so the whole set of four columns is centered, with the cards off to one side.

/* Centers the actual items when the row isn't full */
.logo-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, 160px);
  justify-content: center;
  gap: 2rem;
}

That's a nice pattern for a row of partner logos or badges that should always look centered, however many there are.

Choosing Between Them

Ask one question: when there aren't enough items to fill a row, should they stretch?

Use auto-fit When Items Should Fill the Space

auto-fit is the right choice when a partly filled row should still look complete:

  • A feature section with two, three, or four feature boxes, depending on the page.
  • A pricing table where the number of plans varies.
  • A form layout where a pair of inputs should share the row.
.features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 2rem;
}

With two features, each takes half the width. With three, each takes a third. With six on a wide screen, they wrap into rows.

Use auto-fill When Items Should Keep a Consistent Size

auto-fill is the right choice when every item should look the same size, no matter how many there are:

  • A product listing or search results grid, where a single result shouldn't become a giant card.
  • A photo gallery, where the last row should line up with the rows above it.
  • A dashboard of widgets that should keep a predictable width.
.search-results {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: 1.5rem;
}

If a search returns one product, it stays card-sized instead of stretching across the page. That's usually what users expect from a listing.

Note that with either keyword, the last row of a multi-row grid behaves the same: columns were already sized by the full rows above, so items in an incomplete last row keep their column width. auto-fit only collapses tracks that are empty across the entire grid, which only happens when there's a single partial row.

Making the Minimum Safe on Small Screens

There's a common bug with both keywords. If the container gets narrower than the minimum track size, a single column still has to be at least that wide, so it overflows:

/* On a 320px screen with padding, 360px columns overflow */
.grid {
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
}

Wrap the minimum in min() so it can never exceed the container:

.grid {
  grid-template-columns: repeat(auto-fill, minmax(min(360px, 100%), 1fr));
}

Now the minimum is 360px on larger screens, but on a narrow phone it drops to 100% of the container, and you get a single full-width column with no overflow. This small change is worth adding to every auto-fill or auto-fit grid you write.

Other Gotchas

You can't use auto-fill or auto-fit with a flexible minimum. Something like repeat(auto-fill, 1fr) or repeat(auto-fill, minmax(auto, 1fr)) is invalid, because the browser can't count how many tracks fit if their minimum isn't a fixed size. The minimum must be a length or percentage, or min() and similar functions that resolve to one.

Only one auto-repeat per track list. You can combine an auto-repeat with fixed tracks, like grid-template-columns: 200px repeat(auto-fill, minmax(100px, 1fr)), but you can't have two auto-fill or auto-fit repeats in the same list.

Items that span several columns. If an item has grid-column: span 3 and only two columns fit, grid creates implicit columns to hold it, which can cause overflow. Keep spans small or reset them on narrow screens.

It works for rows too. auto-fill and auto-fit can be used in grid-template-rows, but only when the container has a definite height or max-height. Without one, there's nothing for the browser to divide.

Browser Support

repeat(), auto-fill, auto-fit, and minmax() have been part of CSS Grid since it shipped, and they're supported in every current browser. The min() safety pattern relies on CSS math functions, which are also universally supported today. You can use everything in this guide without a fallback.

Conclusion

auto-fill and auto-fit both create as many columns as fit in the container, and both produce the same layout whenever a row is full. They only differ when there are fewer items than available columns: auto-fill keeps the empty columns, so items stay the same size, while auto-fit collapses them, so items stretch to fill the row.

Pick auto-fit when a partial row should look complete, and auto-fill when items should look consistent regardless of how many there are. And whichever you choose, wrap the minimum in min(..., 100%) so your grid never overflows on small screens.

Tags :
Share :

Related Posts

A Complete Guide to CSS Container Queries

A Complete Guide to CSS Container Queries

For more than a decade, responsive design meant one thing: media queries. You asked the browser how wide the viewport was and adjusted your layout ac

Continue Reading
A Comprehensive Guide to Installing Next.js

A Comprehensive Guide to Installing Next.js

Next.js has emerged as a powerful framework for building React applications, offering features like server-side rendering, static site generation, an

Continue Reading
Advanced CSS with clamp(), min(), and max(): Simplifying Dynamic Styling

Advanced CSS with clamp(), min(), and max(): Simplifying Dynamic Styling

CSS has evolved significantly, and modern tools like clamp(), min(), and max() are powerful game-changers in dynamic styling. If you’ve struggl

Continue Reading