Template Literals & Tagged Templates

Difficulty: Beginner

Template literals, introduced in ES6, are string literals delimited by backticks (`) instead of single or double quotes. They provide two major improvements over traditional strings: expression interpolation using ${...} syntax and native multi-line string support. These features make string construction cleaner, more readable, and less error-prone than manual concatenation.

Expression interpolation is the most commonly used feature of template literals. Any valid JavaScript expression placed inside ${...} is evaluated and converted to a string. This includes variables, function calls, arithmetic operations, ternary expressions, and even other template literals. The expressions are evaluated in the surrounding scope, so you have full access to all variables and functions available at that point in your code.

Multi-line strings with template literals preserve actual line breaks in the source code. With traditional strings, you needed escape characters (\n) or array joining to create multi-line content. Template literals let you write the string exactly as it should appear, making it much easier to construct HTML templates, SQL queries, or any formatted text directly in your code.

Tagged template literals are a more advanced feature that lets you process template literals with a custom function. A tag function receives the static string parts as an array and the interpolated values as separate arguments. This opens up powerful patterns like automatic escaping for HTML or SQL injection prevention, internationalization, styled-components in React, and custom string formatting.

String.raw is a built-in tag function that returns the raw string without processing escape sequences. This is particularly useful when working with regular expressions, Windows file paths, or any string where you want backslashes to remain as literal characters. String.raw`\n` produces the two characters \ and n, not a newline.

In practice, template literals have largely replaced string concatenation in modern JavaScript. They're used extensively in frameworks like React (JSX expressions), testing libraries (snapshot descriptions), logging, and template engines. The tagged template pattern powers libraries like styled-components, GraphQL's gql tag, and lit-html, demonstrating how a simple language feature can enable entirely new programming paradigms.

Code examples

Basic interpolation and multi-line strings

const name = 'Alice';
const age = 28;
const role = 'Developer';

// Expression interpolation
console.log(`Hello, ${name}! You are ${age} years old.`);
console.log(`Next year you'll be ${age + 1}.`);
console.log(`Role: ${role.toUpperCase()}`);
console.log(`Status: ${age >= 18 ? 'Adult' : 'Minor'}`);

// Multi-line strings
const html = `
<div class="card">
  <h2>${name}</h2>
  <p>Age: ${age}</p>
  <span>${role}</span>
</div>
`;
console.log(html);

// Nested template literals
const items = ['Apple', 'Banana', 'Cherry'];
const list = `<ul>
${items.map(item => `  <li>${item}</li>`).join('\n')}
</ul>`;
console.log(list);

Template literals support any JavaScript expression inside ${}. Multi-line strings preserve formatting. You can even nest template literals inside interpolation expressions, as shown in the list-building example.

Tagged template literals

// A tag function receives strings array and values
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    const value = values[i] !== undefined ? `${values[i]}` : '';
    return result + str + value;
  }, '');
}

const product = 'JavaScript Course';
const price = 49.99;
console.log(highlight`Buy ${product} for only ${price} today!`);

// HTML escaping tag
function safeHTML(strings, ...values) {
  const escape = (str) => String(str)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
  
  return strings.reduce((result, str, i) => {
    const value = values[i] !== undefined ? escape(values[i]) : '';
    return result + str + value;
  }, '');
}

const userInput = '<script>alert("XSS")</script>';
console.log(safeHTML`<p>User said: ${userInput}</p>`);

The highlight tag wraps interpolated values in asterisks. The safeHTML tag escapes HTML special characters in values, preventing XSS attacks. Both demonstrate how tag functions receive the static string parts and dynamic values separately.

String.raw and practical tag patterns

// String.raw preserves escape sequences as-is
console.log(`Line 1\nLine 2`);
console.log(String.raw`Line 1\nLine 2`);

// Useful for regex patterns
const pattern = String.raw`\d+\.\d+`;
console.log('Pattern:', pattern);
console.log(new RegExp(pattern).test('3.14'));

// Simple i18n tag
const translations = {
  'Hello, !': 'Hola, !',
  'You have  items': 'Tienes  articulos'
};

function i18n(strings, ...values) {
  const key = strings.join(' ');
  const template = translations[key] || key;
  const parts = template.split(' ');
  
  let result = '';
  let valueIndex = 0;
  for (const part of parts) {
    if (part === '' && valueIndex < values.length) {
      result += values[valueIndex++];
    } else {
      result += (result ? ' ' : '') + part;
    }
  }
  return result;
}

const user = 'Carlos';
console.log(`Hello, ${user}!`);

String.raw prevents escape sequence processing, keeping backslashes literal. This is essential for regex patterns where you'd otherwise need double escaping. The i18n example shows how tagged templates can enable translation systems.

Key points

Concepts covered

template literals, string interpolation, multi-line strings, tagged templates, String.raw