Preserving white space and line breaks in a string in HTML
By Flavio
Published · Updated
Tagged: HTML
Learn how to preserve white space and line breaks in an HTML string with the CSS white-space pre-wrap property, or the whitespace-pre-wrap class in Tailwind.
In a project I was rendering the description of a job which I got through a <textarea> field in a form, and I stored that in the database.
Now, this description was not interpreted as HTML, and when I added it to the page, the white space and line breaks were not respected by the browser.
I wanted this:

but I got this:

The solution was to add this CSS instruction:
white-space: pre-wrap;
which using Tailwind corresponds to the whitespace-pre-wrap class.
Why the browser removes line breaks
By default, HTML text uses white-space: normal. With it, the browser turns every line break, tab and group of spaces into a single space. That’s what lets you indent your HTML however you want without changing how the page looks.
A line break in your text is just a white space character, so it gets collapsed too. Only a <br> tag or a new block element (like a <p>) starts a new line.
white-space: pre-wrap tells the browser to keep line breaks and spaces as they are, and still wrap long lines to fit the box.
Which value to use
There are 3 values that keep line breaks:
| Value | Line breaks | Multiple spaces | Long lines |
|---|---|---|---|
pre | kept | kept | don’t wrap |
pre-wrap | kept | kept | wrap |
pre-line | kept | collapsed into one | wrap |
pre-wrapis the one I use for text that users typed in a textarea.pre-lineis good when you want the line breaks, but not the extra spaces people add by mistake.prekeeps everything and never wraps, so long lines run out of the box. It’s the default style of the<pre>tag, which is meant for code.
In Tailwind the classes are whitespace-pre, whitespace-pre-wrap and whitespace-pre-line.
Watch the white space in your template
With pre-wrap the browser keeps all white space inside the element. That includes the indentation and line breaks of your own template. If you write this:
<p class="whitespace-pre-wrap">
{{ description }}
</p>
you get an empty line at the start and 2 spaces before the text. Put the variable right next to the tags:
<p class="whitespace-pre-wrap">{{ description }}</p>
Also check what’s stored in the database. If the text has spaces or empty lines at the start or end, they’ll show up now. Call .trim() on it before saving, or before rendering.
It works the same in React
In JSX, {description} renders the text as is, and the line breaks get collapsed like in plain HTML. Add the same class or style:
<p className="whitespace-pre-wrap">{description}</p>
The alternative: replace line breaks with <br>
Another way is to turn each line break into a <br> tag and insert the result as HTML.
Don’t do this directly on user text. If someone types <script> in the textarea, you’d add it to your page. Escape the HTML characters first:
function escapeHtml(text) {
return text
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", ''')
}
const html = escapeHtml(description).replace(/\r?\n/g, '<br>')
The \r?\n matches both \n and \r\n line breaks.
This works, but you have to remember to escape every time. With the CSS solution the text stays plain text, and your template engine (or React) escapes it for you. So I prefer white-space: pre-wrap.
Want me to talk about your product? You can sponsor this site.