52

Trying to use handlebars.js for templating but the library seems to ignore newlines.

What is the correct way to deal with newlines? Should they be replaced manually after the templating action?

Uri
  • 25,622
  • 10
  • 45
  • 72

7 Answers7

104

It doesn't do so automatically, but using the helpers feature this can be achieved:

JS:

Handlebars.registerHelper('breaklines', function(text) {
    text = Handlebars.Utils.escapeExpression(text);
    text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
    return new Handlebars.SafeString(text);
});

HTML template:

<div>
    {{breaklines description}}
</div>
gerry3
  • 21,420
  • 9
  • 66
  • 74
Uri
  • 25,622
  • 10
  • 45
  • 72
  • 3
    To use this in Meteor, the immediately-invoked function expression that defines Handlebars.Utils has to be copied and pasted into an in-app .js file. For whatever reason, it's not normally accessible from within Meteor. It's located in `.meteor/local/build/server/node_modules/handlebars/lib/handlebars/utils.js`. – wizonesolutions Jul 17 '13 at 16:05
  • 2
    Why do we need the `toString()`? escapeExpression already returns a string, no? – Jonatan Littke Oct 15 '13 at 09:19
  • 1
    For Ember, I had to use `Ember.Handlebars.registerBoundHelper` – gerry3 Feb 24 '15 at 01:28
  • This answer does not work and is out of date...see the triple curly braces answer below – Ben Summerhayes Mar 18 '17 at 22:40
  • Where do I have to do this? I get `Handlebars is not defined` – Post Self Nov 03 '17 at 12:17
  • where do we add this code ? I did alot of searching , but couldn't find where to write this. Tried script tag in html. Also tried a function controller.ts (working on Nestjs) – Adeel Shekhani Mar 08 '21 at 19:18
30

By inserting three braces instead of the conventional two, you can instruct handlebars to halt its normal techniques of escaping html expressions such as <br> and <p>;

For example, from the handlebars website:

"Handlebars HTML-escapes values returned by a {{expression}}. If you don't want Handlebars to escape a value, use the "triple-stash", {{{."

    <div class="entry">
      <h1>{{title}}</h1>
      <div class="body">
        {{{body}}}
      </div>
    </div>

with this context:

    {
      title: "All about <p> Tags",
      body: "<p>This is a post about &lt;p&gt; tags</p>"
    }

results in:

    <div class="entry">
      <h1>All About &lt;p&gt; Tags</h1>
      <div class="body">
        <p>This is a post about &lt;p&gt; tags</p>
      </div>
    </div>
Brigand
  • 84,529
  • 20
  • 165
  • 173
  • that worked for me - I found it easier to set the line breaks server side, just wanted HB to use them – Leon Jun 26 '14 at 16:04
  • 9
    Except if you are using user generated content from a textarea, no HTML will be escaped at all and you now have an XSS vector as the use could enter script tags. – Steve Swinsburg Mar 12 '15 at 03:35
  • 1
    This needs more upvotes as this solution is much simpler and straightforward. In my case I was using https://github.com/yads/nodemailer-express-handlebars and I would need to import the Handlebar.Utils class (as noted on a comment of another answer). – Xeroxoid Mar 18 '15 at 13:56
  • 1
    Yes, the accepted solution didn't worked for me without hassle. This one did. – Egor Jun 27 '15 at 14:29
  • much better way...the "correct" answer above is out of date and no longer works – Ben Summerhayes Mar 18 '17 at 22:40
  • This is EXACTLY what I was looking for! Thank you! – Scott Oct 29 '17 at 22:09
  • 5
    Wow, so many upvotes for introducing an XSS vector!! The triple-slash expression should only be used, if you're 100% sure about the contents of your template. That is, almonst never! – dpr Dec 15 '17 at 13:56
14

Here are two approaches I prefer over the currently-accepted answer:

  1. Use white-space: pre-wrap; or white-space: pre; on the element where you want to preserve line-breaks (allowing or suppressing natural line wrapping respectively). If you want sequences of whitespace to be collapsed, which is how text in HTML is normally presented, use white-space: pre-line; but note that IE8 and below do not support this. https://developer.mozilla.org/en-US/docs/Web/CSS/white-space
  2. Or here's a version of the template helper that doesn't require any copying and pasting of external code:

    Template.registerHelper('breaklines', function (text) {
      text = Blaze._escape(text);
      text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
      return new Spacebars.SafeString(text);
    });
    

    See https://github.com/meteor/meteor/blob/devel/packages/blaze/preamble.js for Blaze._escape

Matt Jenkins
  • 2,824
  • 1
  • 30
  • 34
3

Simply use the CSS property white-space and set the value as pre-line

For a example in your markup file:

<p style="white-space: pre-line">
  {{text}}
</p>

Then you can pass the text containing break lines as below

{text: "First line

        Second line"
}

The result will look like

First line

Second line
  • This one should be the answer. If you're working with Handlebars and use JSON, just add a line break, aka \n, in your string value, and it'll create the line break. – Bennybear May 26 '23 at 21:41
2

Any solution that uses triple staches will open your application to XSS attacks unless you implement something to sanitize the HTML.

I would suggest using the <pre> tag rather than creating a custom helper.

ryanw51
  • 200
  • 1
  • 1
  • 7
1

I've used the code posted by @Uri and it was very useful.

But I realised that when I register the helper, the function parameter that it receives is not the text, but the function that is called inside the Handlebars template. So first I had to call it to get the text.

In order to clarify, I had to do:

Handlebars.registerHelper('breaklines', function(descriptionFunction) {
    text = descriptionFunction();
    text = Handlebars.Utils.escapeExpression(text);
    text = text.toString();
    text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
    return new Handlebars.SafeString(text);
});

This is the only way I could make it work.

eze.scaruli
  • 1,207
  • 1
  • 13
  • 19
0

For others like me that were following the Getting start code and didn't know why there was no HTML showing up.

in handlebars expressions documentation it states

Handlebars HTML-escapes values returned by a {{expression}}. If you don't want Handlebars to escape a value, use the "triple-stash", {{{

Nagy Wesley
  • 111
  • 2
  • 11