22-Dec

JavaScript

Tagged template literals

Template literals (`My name is ${name}`) have existed in JavaScript for a while. There is a good chance that it is your preferred way to concatenate strings in JavaScript. A slightly lesser known sub-feature of this is the tagged template literal (myTag`My name is ${name}`). What is it and why should you care?

2 min read

·

By Mats Byrkjeland

·

December 22, 2021

In fact, the tag (myTag in the example above) is just a function with a special signature. The first parameter is an array of strings. The following arguments are the substitution values provided. A tag like this, called normal here, would just concatenate the string as expected by a normal template string:

function normal(strings, ...args) {
  return args.map((arg, i) => strings[i] + arg) + strings[args.length];
}

const a = normal`I am ${100} years old.`
// I am 100 years old.

We can have slightly more fun by adding some transformations like making this screaming function:

function scream(strings, ...args) {
  const concatenated = args.map((arg, i) => strings[i] + arg) + strings[args.length];
  return concatenated.toUpperCase()
}

const a = scream`I am ${100} years old.`
// I AM 100 YEARS OLD.

Fun? Yes. But perhaps not very useful. And you could easily just call .toUpperCase() on the template string or create a normal function for it.

The great advantage of tagged template strings is that you can access the individual arguments passed to the string. Let's create a debug tag that prints a table of the arguments and their types if we're not in production mode:

function debug(strings, ...args) {
  // If we're in production, do nothing
  if (process.env.NODE_ENV === "production") {
    return
  }
  
  // The normally concatenated string
  const concatenatedString =
    args.map((arg, i) => strings[i] + arg) + strings[args.length];

  // In non-production environments, log the string to console
  console.log(concatenatedString);

  // Then print a table of all the arguments!
  console.table(args.map((arg) => ({
    Value: arg, 
    Type: typeof arg,
  })));
}

debug`What is ${2} + ${"two"}? Beats me!`;

// What is 2 + two? Beats me!
// ┌─────────┬───────┬──────────┐
// │ (index) │ Value │   Type   │
// ├─────────┼───────┼──────────┤
// │    0    │   2   │ 'number' │
// │    1    │ 'two' │ 'string' │
//└─────────┴───────┴──────────┘

This is perhaps something we could use, leveraging the advantages of the tagged template string syntax. Notice that we don't need to return a string from this tag function. You can return whatever you want.

Real-life usages

Tagged template literals are used in some popular libraries out there. You might recognize it from the popular CSS-in-JS library Styled Components, where the tags are used to generate React components with certain styles:

// Create a Wrapper component that'll render a <section> tag with some styles
// From https://styled-components.com/docs/basics
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

If you have used GraphQL and the Apollo libraries, you might have encountered the gql tag. It is a helpful utility for parsing your GraphQL queries, making them easier to work with compared to using simple strings.

Another library which supports the tagged template literal syntax is timeproxy. It lets you create human-readable time values with strings. And with tagged template literals, the syntax is quite compact and readable.

import tp from 'timeproxy';

const TIMEOUT_LIMIT = tp`five seconds`;
const AGE_LIMIT = tp`1 week and 6 days`;
const SESSION_TIMEOUT = tp`${60} minutes`;

Conclusion

Tagged template literals is a syntax that provides a new way of working with strings. Maybe you have some use case in your project that it is perfect for? At least now you know some of the magic behind this funky syntax when you see it!

22-Dec