DEV Community

Cover image for You hand-edit headlines to avoid orphaned words. `text-wrap: balance` does it natively.
Parsa Jiravand
Parsa Jiravand

Posted on

You hand-edit headlines to avoid orphaned words. `text-wrap: balance` does it natively.

Here is a small but persistent annoyance in frontend work:

<h1>The Practical Guide to Building Resilient
Web</h1>
Enter fullscreen mode Exit fullscreen mode

The browser broke the headline at "Web" because it ran out of space. Now you have one word sitting alone on the second line. You fix it by shortening a word, tweaking max-width, or inserting a <br> that will be wrong at a different viewport width anyway.

This is not a rare edge case. It happens every time a heading is near the container edge, and every fix that works at one width breaks at another.

text-wrap: balance is the native solution.

What text-wrap: balance does

text-wrap: balance tells the browser to distribute the text as evenly as possible across all lines, rather than filling each line greedily until it overflows. The browser picks the break points; you don't guess.

h1, h2, h3 {
  text-wrap: balance;
}
Enter fullscreen mode Exit fullscreen mode

Before, with the default greedy wrapping:

The Practical Guide to Building Resilient
Web
Enter fullscreen mode Exit fullscreen mode

After, with text-wrap: balance:

The Practical Guide to
Building Resilient Web
Enter fullscreen mode Exit fullscreen mode

Both lines are closer to equal in length. The orphan disappears. No <br>, no max-width in ch units, no viewport-specific overrides.

The property is limited to short content — the browser caps the lookahead at around six lines. That is intentional: the algorithm is quadratic, and running it on a 500-word block would be expensive. Headlines and pull quotes are exactly the right target.

🎮 Try it yourself

▶️ Open the interactive playground →

Runs right in your browser — poke at it and watch the concept react live.

text-wrap: pretty for body text

Long-form paragraphs have their own orphan problem: a single word stranded at the start of the last line. text-wrap: balance is too aggressive for running text — balancing a 10-line paragraph would shift nearly every line. text-wrap: pretty is the calibrated version.

p, li, blockquote {
  text-wrap: pretty;
}
Enter fullscreen mode Exit fullscreen mode

pretty uses a lookahead only on the last few lines of a block, pulling one word back if it would otherwise sit alone. The result looks identical to balance for the last line, but the rest of the paragraph is untouched.

/* Recommended defaults */
h1, h2, h3, h4, h5, h6 {
  text-wrap: balance;
}

p, li, figcaption, blockquote {
  text-wrap: pretty;
}
Enter fullscreen mode Exit fullscreen mode

This pair covers both problems with two rules and no JavaScript.

Comparison with the old workarounds

<br> tags. Work at one width. Wrong at every other width. Require you to re-edit content when the container changes.

&shy; soft hyphens. Depend on hyphenation, which is language-sensitive and not always what you want for brand names or technical terms.

max-width tweaks. Force the heading into a narrower box to avoid the bad break. Move the break to a different width. Still wrong somewhere.

JavaScript rebalancing (e.g. react-text-balancer). Measures the element after render and inserts zero-width spaces or spans to approximate balance. Requires a ResizeObserver, adds DOM complexity, and causes a repaint.

text-wrap: balance does none of this. The browser solves the line-breaking problem during layout, before anything is painted. There is no DOM surgery, no observer, no flash of unbalanced content.

A real-world example

A marketing page card with a headline and a description:

<article class="card">
  <h2>Automate Your Deployment Pipeline Without the Overhead</h2>
  <p>
    Learn how to wire up a zero-downtime deploy in an afternoon using
    tools your team already has installed.
  </p>
</article>
Enter fullscreen mode Exit fullscreen mode
.card h2 {
  text-wrap: balance;
  font-size: clamp(1.25rem, 2.5vw, 1.75rem);
}

.card p {
  text-wrap: pretty;
}
Enter fullscreen mode Exit fullscreen mode

The h2 distributes across lines evenly regardless of the card's width — useful in a grid where card widths vary by column count. The p avoids a trailing orphan without disrupting the rhythm of the rest of the paragraph. Both properties do their jobs silently while the layout shifts.

Browser support

text-wrap: balance is Baseline 2024: Chrome 114 (June 2023), Firefox 121 (December 2023), Safari 17.4 (March 2024).

text-wrap: pretty shipped slightly later: Chrome 117, Firefox 128, Safari 17.4.

Both degrade gracefully — unsupported browsers use the default greedy wrapping. The text is still readable; it just may have an orphan. That is the correct fallback behavior for a visual refinement.

There is no polyfill needed and nothing to install.

🧠 Test yourself

Think it clicked? Take the 7-question quiz →

Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.

The takeaway

Search your stylesheets for <br> tags inside headings, or for max-width values expressed in ch units that exist specifically to force a nicer headline break. Each one is a layout constraint solving a problem CSS can now handle natively.

Add text-wrap: balance to your heading selectors and text-wrap: pretty to your body text selectors. The browser finds the right breaks at every viewport width, for every heading length, without any JavaScript or content edits on your part.


Thanks for reading! Let's stay connected:

Top comments (0)