The text-wrap: balance and text-wrap: pretty Properties Explained

text-wrap: balance evens out multi-line headlines while text-wrap: pretty prevents widows in body text, replacing JavaScript polyfills with native browser typographic control.

The Assumption That text-wrap: balance Fixes Body Text

Most developers reach for text-wrap: balance the moment they see a ragged edge on a paragraph. Wrong tool. The CSS text-wrap property answers two distinct typographic problems, and mixing them up is why your headlines look worse and your paragraphs still end with a single orphaned word. balance exists for multi-line titles where every line should carry roughly equal length. pretty exists for body copy where the last line must never be a widow. Both ship natively now. Both replace JavaScript hacks you have been maintaining for years.

Balance reached Baseline newly available in May 2024 across all engines, per the MDN Web Docs Baseline feed. Pretty followed into the same status in 2025. You no longer need a polyfill, a server-side line-breaking guess, or a manual
shoved into a title. The engine does the work. Native does not mean free, and it does not mean universal. text-wrap: balance carries a measurable inline-layout cost because the engine runs a binary search over line-breaking possibilities to find the distribution with the shortest maximum line. The Blink design doc spells this out. Apply it to one hero headline and you will not notice. Apply it to fifty cards on a grid and your compositor will.

Know the failure case before you start. Put balance on a single-line element and nothing happens. The property only engages when the text wraps to two or more lines. Put pretty on a three-word paragraph and nothing happens either, since there is no widow to prevent. These are not bugs. They are the property doing exactly what it was specified to do.

text-wrap balance headline typography: The Native Replacement for JavaScript Line Balancing

What Balance Replaces

Headline typography has always been a compromise. You write a title, the engine wraps it, and the last line carries two words while the first line stretches across the full column. The classic fix was a JavaScript polyfill like balance-text, which measured every line, adjusted word spacing, and reflowed the text until the lines matched. That polyfill worked, but it cost a layout pass on every resize, added a flash of unbalanced text before the script ran, and broke if you changed the font after load. text-wrap: balance replaces that entire mechanism with a single declaration.

h1 {
  text-wrap: balance;
  max-width: 40ch;
}

That sample replaces the balance-text polyfill. The engine measures the available inline size, runs its binary search over possible break points, and picks the set of line breaks that makes the longest line as short as possible. The result is a headline where every line is close to the same length, and the last line does not look abandoned. The property applies to block containers and inline boxes. The initial value is auto, so the default wrapping behaviour remains untouched until you opt in.

Performance and Where to Use It

The performance cost is real. The Blink design doc for text-wrap: balance documents that the binary search over line-breaking possibilities adds inline-layout cost proportional to the number of candidate break points. For a single title, that cost is negligible. For a grid of fifty product titles, each one triggering its own search, the layout phase can stretch noticeably, especially on low-end mobile devices. The rule: use balance on the one or two hero headlines per page, not on every card title. If you need uniform card titles and the text length varies wildly, let them wrap unevenly or truncate with a line clamp instead of paying the balance tax fifty times.

Common Mistakes

One common mistake is expecting balance to work across separate block elements. It does not. The property balances lines within a single text block, not across sibling paragraphs. If you have a two-line title inside a and another next to it, balance will not equalise the two blocks. It only sees the text inside each element. Another mistake is applying it to a short title that fits on one line. The property has no effect on single-line text. Nothing to balance. Apply balance unconditionally and accept that single-line titles are untouched.

text-wrap pretty widow prevention: The Native Way to Stop a Single Word on the Last Line

What Pretty Fixes

A widow is the last line of a paragraph carrying a single word, or worse, a single syllable. It looks like a mistake, it wastes vertical space, and it is the first thing a sharp-eyed reader notices. The classic fix was a manual
before the last two words, or a non-breaking space between them, forcing the pair onto the final line. That hack worked until you changed the font size, the viewport width, or the language. Then the
landed in the wrong place and you had a three-line paragraph with a forced break in the middle. text-wrap: pretty replaces that hack with an engine-native rule that prevents widows without hard-coded break points.

article p {
  text-wrap: pretty;
}

That sample replaces the manual
or the non-breaking space hack. The pretty value tells the engine to prefer a line break that avoids leaving a single trailing word on the last line. The engine will pull one or two words up from the final line so that the last line carries at least two words, or it will adjust the break points earlier in the paragraph to make that happen. Unlike balance, pretty does not try to equalise line lengths. It only cares about the final line's word count. That makes it cheaper, because the engine does not run a full binary search over all break points. It evaluates the last few possible break positions and picks the one that avoids the widow.

Where Pretty Belongs

Pretty is the right default for body text. Apply it to every paragraph on the page without the layout cost ballooning. The decision is local to each paragraph's final line. The trade-off: pretty may cause the penultimate line to be slightly looser because the engine pulls a word up to fill the last line. In practice, that looseness is invisible. What you gain is a page where no paragraph ends with a lonely word. The failure case is a paragraph exactly one line long. No widow to prevent, so the property does nothing. Another failure case is a paragraph with a forced break, such as a
you inserted manually. Pretty respects explicit breaks and will not rebalance around them.

CSS native text balancing: What the Property Does and What It Never Will

The Mechanism

CSS native text balancing is the umbrella term for both balance and pretty. Both values are part of the text-wrap shorthand in the CSS Text Module Level 4 specification, still a W3C Working Draft. The specification defines text-wrap: balance | stable | auto, and pretty is an additional value that landed in the same module. The shorthand accepts a single value, and the longhand property text-wrap-style exists for finer control, though in practice you will write the shorthand.

Choosing Deliberately

The key distinction is the typographic problem each value solves. Balance distributes text evenly across all lines, the requirement for a headline that should look like a deliberate composition. Pretty prevents a widow on the final line, the requirement for body copy that should read without a visual stumble. They are not interchangeable. Apply balance to a paragraph and you get equalised lines that may look like a headline, which is wrong for prose. Apply pretty to a title and you get a widow-free title but the lines may be wildly uneven, which is also wrong. The spec does not forbid either misuse, and the engine will happily apply either value. You have to choose deliberately.

Fallback and Limits

The fallback is text-wrap: wrap, the default and the initial value of the shorthand. If an engine does not support balance or pretty, it falls back to normal line breaking, and the text remains readable. No visual disaster, only a ragged edge or a widow you were trying to avoid. That degradation path is acceptable because the content is still legible. The @supports guard lets you apply the balancing only where it is available, but since both values are newly available across all engines since 2025, the guard is mostly useful for older Safari versions locked to device iOS before the feature shipped.

What the property will never do: balance text across multiple block elements, or reflow text inside a flex item with a fixed width unless that width is the item's content size. It also does not interact with hyphens the way you might hope. If hyphenation is enabled, the line-breaking algorithm considers hyphens as valid break points, and balance will include them in its search. That can produce a balanced headline with a hyphenated word, which is typographically acceptable but may surprise you. To forbid hyphens in a balanced title, set hyphens: none explicitly on that element.

text-wrap performance cost: What Balance Actually Costs in Layout, Paint, and Composite

Where the Cost Lands

Every CSS property has a cost, and text-wrap: balance is one of the more expensive text properties you can apply. The cost is not in paint or composite. It is entirely in layout, specifically the inline-layout phase where the engine determines line breaks. The Blink design doc for text-wrap: balance states that the implementation performs a binary search over the number of lines, trying different break point sets until it finds one where the maximum line length is minimized. Each attempt is a full line-breaking pass, so the total cost is roughly O(log n) times the cost of a single pass, where n is the number of candidate break points in the text.

For a short title of twenty words, that is a handful of passes, each taking microseconds. For a long paragraph of two hundred words, the cost grows, but you would not use balance on a paragraph. For a grid of many titles, the cost multiplies per element. Apply balance to the hero headline and the section titles, not to every card on the page. The alternative, pretty, is cheaper because it only evaluates the final few break points to avoid a widow. It does not run a binary search over the whole paragraph, so its cost is closer to a single line-breaking pass plus a small local adjustment.

Layout Shift and Core Web Vitals

There is no documented paint cost or compositor impact for either value. The property changes how text is laid out, but once the lines are set, painting the glyphs is the same regardless of which text-wrap value you used. The composite step is also unaffected. What you may notice is a layout shift if the balanced text causes a different number of lines than the unbalanced text. That shift is a cumulative layout shift contribution, and it is real. If a title balances to three lines instead of two, the elements below it move down. The CLS score rises, and a poor score hurts your Core Web Vitals. Mitigate it by reserving space for the maximum expected line count, or accept that balancing may change the height and plan your layout accordingly.

.card h2 {
  text-wrap: balance; /* costs a binary search per card */
}

.card p {
  text-wrap: pretty;  /* costs a local final-line check */
}

That comparison shows how to apply both on the same component. The title gets balance because it is short and the visual payoff is high. The paragraph gets pretty because it is body copy and the cost is low. If you are building a page with a hundred cards, measure the layout time in the performance panel. If the title balance pushes layout time past your budget, drop it to pretty on the cards and keep balance only on the page-level hero. The text remains readable either way.

The Comparison: balance vs pretty on the Same Heading, and the Single-Line Trap

Side by Side

Seeing the two properties side by side makes the distinction concrete. The same title, wrapped at the same width, produces different line distributions under each value. Balance tries to make every line equal. Pretty leaves the early lines alone and only fixes the last line's word count. The difference is visible when the title has a long word or a short word that creates an obvious imbalance.

h2 {
  text-wrap: balance;
}

h2.alt {
  text-wrap: pretty;
}

With balance, a five-word title might break as three lines of roughly two words each, with the last line carrying two words. With pretty, the same title might break as two lines, the first carrying three words and the second carrying two, which is fine because the last line has two words and is not a widow. The choice depends on whether you want visual symmetry or just widow prevention. For a headline, symmetry reads as intentional design. For a paragraph, symmetry reads as a block of text that looks like a callout, which is wrong.

The Single-Line Trap

The single-line trap is the most common misuse. If an element's text fits on one line, neither balance nor pretty has any effect. The property only engages when the text wraps to a second line. This is documented in the spec, and it is a common source of confusion. Developers test with short titles, see no change, and assume the feature is broken. The correct test: make the container narrow enough that the text wraps, then apply the property. If nothing changes, the title may still be fitting on one line. The failure mode is not a bug. It is the property doing what it was specified to do.

h1 {
  text-wrap: balance;
  /* no effect if the h1 fits on one line */
}

That sample replaces the incorrect assumption that balance works on single lines. If you need a different effect for a single-line title, such as forcing it to wrap, use a different technique: set a max-width and let the text overflow or wrap naturally. That is not what text-wrap is for. The property is purely about how lines break when there are multiple lines, and it is silent otherwise.

text-wrap balance headline typography and text-wrap pretty widow prevention in One Table

Use this table as a quick reference when you are writing CSS and are not sure which value to apply.

| Property value | Typographic problem | Typical element | Cost | Baseline status | |---|---|---|---|---| | text-wrap: balance | Ragged edge on multi-line titles, uneven line lengths | h1, h2, hero titles | Higher: binary search over break points | Widely available since 2024-05-13, per MDN Baseline | | text-wrap: pretty | Widow on the last line of body text | p, article, card body | Lower: local final-line check | Newly available since 2025, per MDN Baseline | | text-wrap: wrap | Normal line breaking, no special handling | Default for all text | None | Always available | | text-wrap: stable | Prevents breaking before a word that would create a widow, but does not rebalance earlier lines | Paragraphs where you want minimal intervention | Low | Part of the same spec, support varies |

The table shows that balance and pretty are not competitors. They are complementary tools for different jobs. Apply both on the same element and the later one in the cascade wins, since text-wrap is a single property. You cannot have balance and pretty at the same time on one element. The practical pattern: balance on titles, pretty on paragraphs, and wrap as the fallback everywhere else.

The Fallback and the @supports Guard: What Happens When the Browser Does Not Care

The Clean Fallback

Every new CSS feature needs a fallback story, and text-wrap: balance and pretty have a clean one. The default value of the text-wrap property is auto, which means normal line breaking. Write text-wrap: balance in an engine that does not support it and the declaration is ignored; the element uses auto. The text still wraps, still reads, and still looks like a normal paragraph. No broken layout, no missing content, no error. The only loss is the visual polish you were trying to add.

@supports (text-wrap: balance) {
  h1 {
    text-wrap: balance;
  }
}

That @supports guard is optional in modern engines, since both values are Baseline newly available since 2025. But if you are supporting an older Safari locked to device iOS, the guard keeps your fallback explicit. Without the guard, the engine ignores the unknown value and uses auto, which is fine. With the guard, you can also provide an alternative, like a wider max-width, that only applies when balance is unsupported. That is progressive enhancement that costs nothing and protects against edge cases.

Being Explicit

The fallback declaration text-wrap: auto is the same as writing nothing, since auto is the initial value. The spec defines text-wrap: balance | stable | auto, and pretty is an additional value in the same module. Writing text-wrap: auto after a balance declaration in a fallback block is redundant but harmless. The more useful pattern: use @supports to apply balance only where it works, and leave the default auto elsewhere. That way you never accidentally apply balance to an element where the binary search cost is unjustified, just because you wrote the declaration without thinking.

FAQ: Four Questions That Come Up When You Start Using text-wrap

Does text-wrap: balance work on single-line text? No. Balance only engages when the text wraps to two or more lines. If the element fits on one line, the property has no effect. This is specified behaviour, not a bug. Test with a narrow container to see the effect.

Is text-wrap: pretty the same as hyphens: auto? No. Pretty controls line breaking to avoid a widow. Hyphens: auto controls whether words can be broken with hyphens. They are independent. You can use both, and pretty will consider hyphenation points when searching for a break that avoids a widow.

What is the performance cost of balance on a large page? Balance runs a binary search over break points, so the cost is proportional to the number of candidate breaks. On a page with many titles, the cost adds up. Use balance on one or two hero titles, and pretty on body text, which is cheaper because it only checks the final line.

Can I use balance and pretty on the same element? No. text-wrap is a single property, so the last value in the cascade wins. To get both effects, apply balance to the title and pretty to the paragraph inside the same component. The element itself can only have one value.

The Honest Caveat: The Browser Is the Final Renderer

Everything here describes what the specification says and what the engines implement today. The honest version: you cannot control the exact line break the engine chooses. You can request balance or pretty, and the engine will do its best within its line-breaking algorithm, influenced by the font metrics, the available width, the language, and the hyphenation settings. If you need a pixel-perfect headline that breaks exactly where you want, the only reliable way is to craft the text yourself, using manual
or separate elements. text-wrap is a high-level instruction, not a fine-grained tool.

The practical consequence: test your balanced titles at the widths your layout actually uses. A title that balances beautifully at a wide viewport may look wrong at a narrow one, where the binary search produces a different break point set. The property is deterministic for a given width, but the width changes, so the result changes. That is not a flaw. It is the cost of native balancing. The alternative, a JavaScript polyfill, had the same behaviour, plus the flash of unbalanced text before it ran. Native balancing removes the flash and the script, and it accepts that the engine's choice is final.

Use these properties where they pay off: balance for the hero headline, pretty for every paragraph. Skip them for one-line elements and for elements where the layout height must be fixed, because balancing can change the number of lines and shift the content below it. The fallback is always the default wrap, and it is never wrong. The Blink design doc explicitly documents the binary search cost of balance, and that cost is why you should never apply balance to every title on a grid of fifty cards.