3 Cool Uses For Tagged Template Literals

in javascript

ES6 Template literals (those strings with backquotes that automatically interpolate variables, such as `hello ${user}`) have become one of my most used JavaScript features.

Tagged template literals work about the same way but let you write your own "tagger" or handler function which does the interpolation. Why would you want to? I could think of 3 reasons. But first the gory details

How Does Tagged Template Literals Work?

A tagged template literal (sometimes referred to as tagged template string) is a template literal that is associated with a custom handler function, called a tag.

Take the following example for normal template literal:

const user = 'Jones';
const text = 'hello world';
const greeting = `1. ${user} said: "${text}"`

console.log(greeting); // prints: 1. Jones said: "hello world"

So far no surprises. But it turns out we can tag it by just writing the name of a handler function before the template literal:

const user = 'Jones';
const text = 'hello world';
const greeting = b`1. ${user} said: "${text}"`

console.log(greeting); 
// prints: 1. <b>Jones</b> said: "<b>hello world</b>"
// (assuming we defined function b as a tag)

In the above code, assuming a function b is defined it will be called automatically with parameters describing the template. The above example is equivalent to this one:

const user = 'Jones';
const text = 'hello world';
const greeting = b(['1. ', ' said: "', '"'], 'Jones', 'hello world')
console.log(greeting);

In both cases a function named b is called with the first argument being an array of strings, and next arguments are the interpolated values passed in.

A simple implementation for b could wrap any variable in a <b></b> HTML tags:

function b(strings, ...values) {
  const result = [];
  strings.forEach(s => {
    result.push(s);
    result.push('<b>' + (values.shift() || '') + '</b>');
  });
  return result.join('');
}

I created a codepen with the above code so you can see it live:

See the Pen yoxMmv by Ynon Perek (@ynonp) on CodePen.

Now as promised let's move on to seeing how people use this feature.

1. Escaping HTML tags

I suspect HTML escape was one of the first use cases that the designers of this feature had in mind. The following code is vulnerable to XSS attack, if user or thePlace are not sanitized:

const p = document.querySelector('#content');
p.innerHTML = `${user} said: <b>We</b> must go to ${thePlace}`;

Using lodash it's easy to escape both and fix the code, if you remember to call escape every time:

const p = document.querySelector('#content');
const safeUser = _.escape(user);
const safePlace = _.escape(thePlace);

p.innerHTML = `${safeUser} said: <b>We</b> must go to ${safePlace}`;

Alternatively define a tag called safe and you'll get escaping right every time:

function safe(strings, ...values) {
  const result = [];
  strings.forEach(s => {
    result.push(s);
    result.push(_.escape(values.shift() || ''));
  });
  return result.join('');
}

p.innerHTML = safe`${user} said: <b>We</b> must go to ${thePlace}`;

I made another live codepen with the above code here. Feel free to try or edit the code:

See the Pen zdJwxy by Ynon Perek (@ynonp) on CodePen.

2. Internationalization / Localization

The library es2015-i18n-tag uses tagged template literals to translate and localize JavaScript texts. It allows you to write code that looks like this:

console.log(i18n`Hello ${ name }, you have ${ amount }:c in your bank account.`)
// Hallo Steffen, Sie haben US$ 1,250.33 auf Ihrem Bankkonto.

What's great here is that it automatically localizes currencies and date formats. Notice the :c after ${amount} in the above literal automatically adds the dollar sign.

This one shows how it works with dates:

const date = new Date(2012, 11, 20, 19, 0, 0);
i18n`The date is ${date}:t(D).`

// The date is Thursday, December 20, 2012.

Compare that to a function call one would need to write to get the same result:

i18n(['The date is ', ':t(D)'.], date);

And you can see why I'll take the tagged template literal version any day.

3. Writing CSS in JS (For React)

React developers are familiar with React's weird syntax for inline style (written as an object). This React snippet defines a paragraph with red color and orange border:

<p style={{ color: 'red', border: '1px solid orange' }}>Hello World</p>

Most IDEs are not familiar with this syntax and so we lose autocomplete and linting for CSS code.

A library called styled-components aims to fix inline style in React using tagged template literals. Here's what their code looks like, for the same paragraph:

const Content = styled.p`
	color: red;
	border: 1px solid orange
`;

<Content>Hello World</Content>

This gives us back the tool support we lost with inline styles as objects, and tons of other features. Their Getting Started page shows more code examples and live playground to write your own styled components.

Final Thoughts

For a long time I felt tagged template literals had no use in real life, but as I found more libraries that use them I'm starting to change my mind.

Will it remain an asoteric feature for bored library authors or will we see it going mainstream? Still hard to tell.

If you have other ideas or uses for tagged template literals, do share by leaving a comment.

Comments