min-content, max-content, and fit-content() Explained

CSS sizing keywords like min-content, max-content, and fit-content() describe a box's width in terms of what it contains rather than a fixed length or a percentage of its parent. They are the vocabulary of intrinsic sizing, and knowing exactly what each one computes turns a category of fiddly layout problems — auto-width buttons, shrink-to-fit sidebars, label columns that never wrap awkwardly — into one-line declarations. This guide explains what each keyword evaluates to and where it is the right tool. It is part of Intrinsic Sizing Techniques within Mastering Container Queries & Responsive Layouts.

Problem statement

The recurring situation is an element whose correct width is a fact about its contents that you do not know at authoring time. A settings page has a column of labels — "Name", "Email", "Two-factor authentication method" — and the column should be exactly as wide as the longest one, no wider, but must not blow out if a translation triples the string length. A toolbar has buttons that must hug their own text so they read as distinct controls rather than as stretched blocks. A definition list has terms that should never wrap mid-word while their definitions flow freely. In every case a fixed pixel width is a guess that goes stale the moment the copy changes or the page is localised, and a percentage is worse because it is derived from the parent and therefore knows nothing at all about the content. What you actually want is to ask the content how much room it needs. That is precisely what these keywords do.

Why intrinsic keywords instead of fixed widths or percentages

The common alternatives each have a failure mode. A fixed width ignores the content and either clips it or leaves dead space. A percentage width tracks the parent but is blind to what is inside the box, so a tiny label and a paragraph get the same width. The intrinsic keywords flip the relationship: they ask the content how much room it needs and size the box from the answer, which is precisely what you want for elements whose size should follow their text.

There is no JavaScript measurement involved, which matters for both performance and correctness. A common pattern people reach for is reading scrollWidth in script to size an element to its content; that forces a synchronous layout and runs after paint, so it can flash. The intrinsic keywords are resolved by the engine during layout itself, so the box is correct on first paint with no measurement pass and no flash. Accessibility benefits too: because the box sizes to real content, text reflows naturally under zoom instead of being trapped at a scripted pixel width that ignores the larger glyphs. Localisation benefits for the same reason — a German or Finnish translation of a button label simply produces a wider button rather than an overflowing one.

The cost is that these keywords can produce surprising widths if you do not know their definitions — a min-content box can collapse to one long word, and a max-content box can blow past its parent. The fix is understanding, not avoidance.


What each keyword computes

The three keywords answer three different questions about the same content:

  • min-content is the smallest width the box can take without its content overflowing. Practically, it is the width of the widest unbreakable unit — the longest word, the longest URL, the widest replaced element. Text wraps at every opportunity.
  • max-content is the box's preferred width: how wide it would be if nothing ever wrapped, as on an infinitely wide canvas. For a paragraph that is the full text on one line.
  • fit-content(limit) is a hybrid: it behaves like max-content until the content would exceed limit, then it caps at limit and lets the content wrap. The bare fit-content keyword is equivalent to fit-content(stretch), clamping the preferred size to the available space. Formally it is min(max-content, max(min-content, available-or-limit)).

Two clarifications save a lot of confusion. First, these are values, not a separate mechanism: they are accepted anywhere a <length> is, including width, min-width, max-width, their logical equivalents inline-size and friends, and grid track functions. Second, they work in the block axis too. height: min-content on a box asks how short it can be without overflowing, which is genuinely useful for a caption strip or a collapsed panel, though block-axis intrinsic sizing is less commonly needed because block boxes already grow to fit their content by default.

It is also worth separating these from auto, which people reach for interchangeably and which behaves differently depending on context. On a normal-flow block element, width: auto fills the containing block — the opposite of shrink-wrapping. On a floated or absolutely positioned element, width: auto behaves like fit-content. Writing fit-content explicitly means you get shrink-wrap behaviour regardless of how the element is positioned, which is why it is the more predictable choice.

The implementation below renders the same paragraph in four boxes so you can see the differences directly.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
  body { font-family: system-ui, sans-serif; margin: 2rem; max-width: 40rem; }

  .box {
    border: 2px solid #7aa2ff;
    padding: 0.5rem;
    margin-bottom: 1.5rem;
    background: #7aa2ff14;
  }
  .label { font: 600 0.75rem ui-monospace, monospace; color: #555; margin: 0 0 0.25rem; }
  .box p { margin: 0; line-height: 1.4; }

  /* Collapses to the widest single word; everything else wraps */
  .min  { width: min-content; }

  /* Preferred width: the whole sentence on one line, even past the parent */
  .max  { width: max-content; }

  /* max-content until it would exceed 18rem, then caps and wraps */
  .fit  { width: fit-content(18rem); }

  /* Bare keyword: shrink-wrap, but never exceed the available space */
  .fitbare { width: fit-content; }

  /* Shrink-wrapped AND centred: impossible with width: auto, which
     would fill the parent and leave the auto margins nothing to do. */
  .centred { width: fit-content; margin-inline: auto; }
</style>
</head>
<body>
  <div class="box min">
    <p class="label">width: min-content</p>
    <p>Intrinsic sizing keywords describe width by content.</p>
  </div>

  <div class="box max">
    <p class="label">width: max-content</p>
    <p>Intrinsic sizing keywords describe width by content.</p>
  </div>

  <div class="box fit">
    <p class="label">width: fit-content(18rem)</p>
    <p>Intrinsic sizing keywords describe width by content.</p>
  </div>

  <div class="box fitbare">
    <p class="label">width: fit-content</p>
    <p>Intrinsic sizing keywords describe width by content.</p>
  </div>

  <div class="box centred">
    <p class="label">width: fit-content; margin-inline: auto</p>
    <p>Shrink-wrapped, then centred by the auto margins.</p>
  </div>
</body>
</html>

The min-content box narrows until its tallest, thinnest column of wrapped text fits the longest word; max-content stretches the sentence onto one line and can overflow the page; fit-content(18rem) shrink-wraps but stops at 18rem; bare fit-content shrink-wraps but never exceeds the body width. The last box shows the pairing that makes fit-content worth knowing on its own: because the box is now narrower than its parent, margin-inline: auto has leftover space to distribute and the block centres.

Same content under three intrinsic widths Three boxes containing identical text: a narrow min-content box, a wide overflowing max-content box, and a capped fit-content box in between. same content, three widths wraps min-content one line, may overflow parent max-content grows to content, capped at limit fit-content(limit)

Key technique: fit-content() as a grid track

The single most valuable place these keywords appear is grid track sizing, where fit-content() solves the classic label-column problem. A track defined as fit-content(12rem) sizes itself to its widest cell content but refuses to exceed 12rem, so a sidebar of labels is as wide as the longest label needs and no wider, yet a freakishly long label wraps instead of pushing the main column off-screen.

.layout {
  display: grid;
  /* label column shrink-wraps up to 12rem; content takes the rest */
  grid-template-columns: fit-content(12rem) 1fr;
  gap: 1rem;
}

This is impossible to express cleanly with fixed or percentage widths, because the correct width depends on the content. It is worth contrasting fit-content(12rem) against the neighbouring track functions to see exactly what it buys you. A bare auto track sizes to max-content when there is room and shrinks toward min-content under pressure, so it has no cap and a long label can dominate the row. A max-content track never shrinks at all. A min-content track collapses to the longest word immediately, which usually reads as broken. fit-content(limit) is the only one of the four that expresses "as wide as it needs, up to here", which is what a label column actually wants.

The keywords also feed into how flex and grid items compute their automatic minimum size, which is a distinct subject with its own set of symptoms and fixes — see preventing flex and grid overflow for that. The connection worth carrying here is just that min-content is the value the engine reaches for when it needs a floor, which is why understanding what it computes explains behaviour you will meet elsewhere.


Variation: RTL and a max-content button row

max-content shines for a row of buttons that must each hug their label, never stretch, and stay correct in right-to-left layouts. Setting each control to width: max-content makes it exactly as wide as its text plus padding, and because it is content-driven it mirrors correctly under direction: rtl with no extra rules.

.toolbar {
  display: flex;
  gap: 0.5rem;
}
.toolbar button {
  width: max-content;   /* hugs its own label, never stretches */
  padding-inline: 1rem; /* logical padding flips automatically for RTL */
}
.toolbar[dir="rtl"] {
  /* no width changes needed; max-content + logical padding handle it */
  flex-direction: row-reverse;
}

/* A label column that is content-sized in the block direction too:
   logical properties keep this correct in vertical writing modes. */
.definition-list {
  display: grid;
  grid-template-columns: fit-content(14rem) 1fr;
  column-gap: 1rem;
}
.definition-list dt {
  inline-size: max-content;   /* terms never wrap mid-phrase */
  max-inline-size: 100%;      /* but still yield if the track caps them */
}

Using padding-inline rather than padding-left/padding-right keeps the hugging behaviour symmetric in both writing directions, and using inline-size rather than width keeps the definition-list rule correct if the document ever switches to a vertical writing mode — the intrinsic keywords are axis-agnostic, so they pair naturally with logical properties. The max-inline-size: 100% is the safety valve: it lets the max-content term yield to the track cap rather than overflowing it, which is the combination you want whenever an intrinsic width meets a constrained parent.


Browser support note

The intrinsic sizing keywords are among the oldest features on this site. min-content and max-content are supported as width and height values in Chrome 46+, Edge 79+, Firefox 66+, and Safari 11+; the bare fit-content keyword in Chrome 46+, Edge 79+, Firefox 94+, and Safari 11+. That means every engine in circulation has had them for years, and they need no feature gate, no @supports wrapper, and no fallback declaration. The fit-content() function used as a grid track sizing function, and the logical equivalents (inline-size, max-inline-size) these examples pair them with, are likewise available in every current engine. Write them directly.


FAQ

What is the difference between min-content and max-content?min-content is the smallest a box can be without overflowing its content, roughly the width of its longest unbreakable word. max-content is the box's preferred width with no wrapping at all, the width it would take on an infinitely wide canvas.

When should I use fit-content() instead of max-content? Use fit-content() when you want a box to shrink-wrap its content but never exceed a cap. It behaves like max-content until the content reaches the limit you pass, then it stops growing and wraps, which max-content never does.

Does fit-content() work as a grid track size? Yes. fit-content(limit) is a valid grid track sizing function: the track sizes to its content up to the limit, then caps. It is one of the most useful sidebar and label-column track definitions.

Why does my width: min-content element look too narrow? Because min-content collapses to the longest unbreakable run of content. Long words, URLs, or non-breaking phrases set the floor; if there are none, the box can become as narrow as a single character.

How is width: fit-content different from width: auto on a block element?width: auto makes a block element fill its containing block. width: fit-content shrink-wraps it to its content instead, capped at the available space, which is why fit-content plus margin-inline: auto centres a block without a fixed width.


Related articles

More pages in the same section.