How to keep line breaks in user input without unescaping it in Laravel

The problem

I recently got a question from a client about disappearing line breaks. It was a valid question since the text came from a textarea input that was echoed out on a page. The type of content was really simple and had no need for markup, so no WYSIWYG or Markdown editor was necessary. However, line breaks were really important in this case.

PHP offers the nl2br() function that has been around since PHP 4. This function will return a string with <br /> or <br> inserted before all newlines (\r\n, \n\r, \n and \r). Just what we need! Let's put it to work with this variable for this example:

$description = 'This is line one\nThis is line 2\nThis is line 3';
    {{ nl2br($description) }}

Nope! That will output: This is line one<br /> This is line 2<br />This is line 3 because we've been using the {{ }} statements. Blade {{ }} statements are automatically sent through PHP's htmlspecialchars function to prevent XSS attacks. By using {!! !!} statements, we can completely skip the unescaping and get the result we want, right? No, that is a terrible idea! Our code would then be vulnerable to XSS attacks.

Here is a really simple rule of thumb: if you are in need of using the {!! !!} statement, take a step back and ask yourself: Where does the content come from? If the content comes from some kind of form or other user input, you should NEVER use it to protect yourself against XSS attacks. There are plenty of valid reasons for using {!! !!} statements, but not in our case.

The solution

So what can we do? Well, in my knowledge there are two simple solutions here:

  • Make use of CSS
  • Make use of PHP

Solve it with CSS

The CSS solution is incredibly simple. We can make use of the white-space CSS property. It is very safe to use since it's supported for a very long time by all major browsers.

<div style="white-space: pre-line">
    {{ $description }}

Solve it with PHP

If you're not able to use the CSS solution for whatever reason, there’s always the good old PHP to the rescue. Writing PHP makes me happy, so let's get started! We could explode on the line breaks and create an array of single lines. Then we can iterate over them and output them with the safe {{ }} statements. To make sure we explode on the line breaks, we can make use of the PHP_EOL constant.

@foreach(explode(PHP_EOL, $description) as $line )
    {{ $line }} <br>

Bonus: solve it in an anonymous Blade component

I love Blade components, they clean up the view files and make it so much better readable and prevent unneeded repetition.

\\ Blade component

@foreach(explode(PHP_EOL, $content) as $line)
    {{ $line }} <br>

\\ Usage
<x-safe-line-breaks :content="$description"/>