Using Tagged Template Literals to escape embedded HTML

JavaScript template strings are great for building HTML templates.

But there’s a problem: embedding a template string into your page can be a security risk

// This is risky
element.innerHTML = `<div id="page">${userDefinedContent}</div>`;

But there’s a super-convenient feature of template strings called “Tagged Template Literals.” It looks like this:

// We're appending our template string onto something called safeHTML...
element.innerHTML = safeHTML`<div id="page">${userDefinedContent}</div>`;

To be clear, safeHTML isn’t a language feature. It’s just an arbitrary function name. So the code above is functionally the same as this:

// Pass our template through safeHTML before assigning it to innerHTML
element.innerHTML = safeHTML(templateData, userDefinedContent);

So if we have a function named safeHTML, we can use it to escape the user content before embedding it. There are a couple ways we could do this:

  • Option 1: Define the safeHTML escaping function ourselves. There are examples online we can use to help us (like this one).
  • Option 2: Pull in a 3rd-party function that does the escaping for us.

For option 2, I found a small library called html-template-tag which you can use like this:

import html from 'html-template-tag';

// Now, userDefinedContent will be escaped, without affecting our wrapper div.
element.innerHTML = html`<div id="page">${userDefinedContent}</div>`;

This escaping function is great if you don’t want any user-defined HTML in your page.

Sometimes you do want user-defined HTML though, like basic text formatting, for example. In that case, you’d need a different function that allows some HTML tags to remain unescaped. Different use-cases will require some combination or escaping, filtering, validating, or sanitizing. Fortunately, there are tons of tagged-template modules out there that can help with these scenarios.

You can do lots of other things with tagged template literals, with libraries doing everything from stripping indentation to building a Virtual DOM. Hopefully this is a nice introduction to the options that are out there.