Don't Use Rails simple_format for Newlines

April 4, 2021

The default behavior for HTML is to collapse white space and treat newline characters the same as spaces.

<%= "Line 1\nLine 2" %>

This renders as:

Line 1 Line 2

Rails provides a helper method called simple_format which replaces newlines with <br> tags. At a glance, this seems like a quick way to deal with newlines:

simple_format("Line 1\nLine 2")
 => "<p>Line 1\n<br />Line 2</p>"

This renders as:

Line 1
Line 2

But the behavior of simple_format isn't simple.

It uses the Rails::Html::SafeListSanitizer to sanitize the input. By default, it permits a small set of allowed elements:

Rails::Html::SafeListSanitizer.allowed_tags
 => #<Set: {"strong", "em", "b", "i", "p", "code", "pre", ...

<b> is allowed because it's safe. Other elements, like <script>, are not because they can lead to HTML injection.

Unfortunately, this approach of stripping out elements can have unexpected results if a user happens to include a < character next to a letter or symbol. For example, everything starting from the < and onward is lost here:

simple_format("What will print? <--- This is a good question.")
 => "<p>What will print? </p>"

This displays as:

What will print?

The sanitizer can also introduce HTML where it didn't exist previously:

simple_format("If a<b and c>b is a<c?")
 => "<p>If a<b>b is a</b></p>"

This has "fixed" the HTML and has turned the <b into a complete <b> element. It omits text and is partially rendered in bold:

If ab is a

To escape HTML, the suggestion for text helpers is to use h before calling them like:

simple_format(h("If a<b and c>b is a<c?"))
 => "<p>If a&lt;b and c&gt;b is a&lt;c?</p>"

Now the full text will display correcly:

If a<b and c>b is a<c?

But this is clunky and it's easy to forget to include the h. The default behavior for view templates was changed way back in Rails 3.0 to always escape HTML output. simple_format should behave similarly and escape HTML by default.

Several GitHub issues related to this have been closed over the past decade (#2935, #6712,#19944, #23230), so it's unlikely to change. But this odd behavior is something that new Rails developers are going to have to continue to look into over and over again.

I've stopped using simple_format entirely. Whenever I need to render newlines, I now use this CSS instead:

white-space: pre-wrap;

References