Home Module 13 Template Literals

1. Introduction

Template literals (also called template strings) use backticks (`) instead of quotes. They unlock three powerful features: string interpolation (embed variables and expressions directly), multi-line strings (no more \n), and tagged templates (pass the string to a function for custom processing).

2. Theory

2.1 String interpolation

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

// Old — concatenation
const greeting1 = 'Hello, ' + name + '! You are ' + age + ' years old.';

// Template literal — cleaner, fewer quotes
const greeting2 = `Hello, ${name}! You are ${age} years old.`;

// Any expression works inside ${}
const a = 5;
const b = 3;
console.log(`${a} + ${b} = ${a + b}`);           // "5 + 3 = 8"
console.log(`Is adult: ${age >= 18}`);            // "Is adult: true"
console.log(`Upper: ${name.toUpperCase()}`);      // "Upper: ALICE"
console.log(`Tax: $${(99.99 * 1.2).toFixed(2)}`); // "Tax: $119.99"

2.2 Multi-line strings

// Old — escape sequences needed
const old = 'Line one\n' +
            'Line two\n' +
            'Line three';

// Template literal — actual newlines
const modern = `Line one
Line two
Line three`;

// HTML template — very common pattern
const card = `
  <div class="card">
    <h2>${title}</h2>
    <p>${description}</p>
  </div>
`;

2.3 Expressions inside interpolation

const user    = { name: 'Alice', role: 'admin', score: 87 };
const isAdmin = user.role === 'admin';

// Ternary inside template
const label = `Status: ${isAdmin ? 'Administrator' : 'Member'}`;

// Function call inside template
function formatDate(ts) {
  return new Date(ts).toLocaleDateString('en-GB');
}
const msg = `Last login: ${formatDate(Date.now())}`;

// Nested template literals
const table = `
  <table>
    ${['Alice', 'Bob', 'Carol'].map(name => `<tr><td>${name}</td></tr>`).join('\n    ')}
  </table>
`;

2.4 Building HTML strings with template literals

// Render a product card
function renderProduct({ name, price, inStock, imageUrl }) {
  return `
    <div class="product-card ${inStock ? '' : 'out-of-stock'}">
      <img src="${imageUrl}" alt="${name}">
      <h3>${name}</h3>
      <p class="price">$${price.toFixed(2)}</p>
      <button ${inStock ? '' : 'disabled'}>
        ${inStock ? 'Add to Cart' : 'Out of Stock'}
      </button>
    </div>
  `;
}

// Render a list
function renderList(items) {
  if (!items.length) return '<p>No items found.</p>';
  return `
    <ul>
      ${items.map(item => `<li>${item}</li>`).join('')}
    </ul>
  `;
}

// IMPORTANT: only use this with TRUSTED data — never user input
// User input must use textContent, not innerHTML

2.5 Tagged templates

A tag is a function placed before the backtick. It receives the string parts and interpolated values as separate arguments — allowing custom processing.

// Tag function signature: (strings, ...values)
function tag(strings, ...values) {
  console.log(strings); // array of literal string parts
  console.log(values);  // array of interpolated values
  return strings.reduce((result, str, i) =>
    result + str + (values[i] !== undefined ? values[i] : ''), '');
}

const a = 5;
const b = 10;
tag`Sum of ${a} and ${b} is ${a + b}`;
// strings: ['Sum of ', ' and ', ' is ', '']
// values:  [5, 10, 15]

// Practical: CSS-in-JS (like the popular styled-components library)
// const Button = styled.button`
//   color: ${props => props.primary ? 'white' : 'black'};
//   background: ${props => props.primary ? 'blue' : 'white'};
// `;

2.6 String.raw — tagged template for raw strings

// String.raw — ignores escape sequences
const path1 = `C:\Users\Alice\Documents`; // \U and \A are escape sequences
const path2 = String.raw`C:\Users\Alice\Documents`; // literal backslashes

console.log(path1); // C:SersliceSocuments (escape sequences interpreted)
console.log(path2); // C:\Users\Alice\Documents (raw)

// Useful for regex patterns and Windows file paths

2.7 Template literals vs string concatenation

// Performance: both are equivalent in modern engines
// Readability: template literals win for multi-variable strings

// Concatenation — hard to read with many variables
const msg1 = 'Hello ' + firstName + ' ' + lastName + ', you have ' + count + ' messages.';

// Template literal — clear and linear
const msg2 = `Hello ${firstName} ${lastName}, you have ${count} messages.`;

// When to still use concatenation:
// - Simple one-variable cases (either is fine)
// - Very long string building in tight loops (rare — profile first)

3. Real World Example

// Email template generator
function generateEmail({ recipientName, senderName, subject, body, unsubscribeUrl }) {
  return `
<!DOCTYPE html>
<html>
<head>
  <title>${subject}</title>
</head>
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto">
  <h1>Hello, ${recipientName}!</h1>
  <div>${body}</div>
  <hr>
  <footer style="font-size:12px; color:#888">
    <p>Sent by ${senderName}</p>
    <p><a href="${unsubscribeUrl}">Unsubscribe</a></p>
  </footer>
</body>
</html>
  `.trim();
}

// SQL query builder (illustrative — use parameterised queries in production)
function buildSelectQuery({ table, fields = ['*'], where = null, limit = null }) {
  const fieldList = Array.isArray(fields) ? fields.join(', ') : fields;
  const whereClause = where ? `WHERE ${where}` : '';
  const limitClause = limit ? `LIMIT ${limit}` : '';
  return `SELECT ${fieldList} FROM ${table} ${whereClause} ${limitClause}`.trim();
}

console.log(buildSelectQuery({ table: 'users', fields: ['id', 'name', 'email'], where: 'active = 1', limit: 10 }));
// SELECT id, name, email FROM users WHERE active = 1 LIMIT 10

4. Code Example

<input id="name-in"  placeholder="Name">
<input id="score-in" placeholder="Score (0-100)" type="number">
<button id="gen">Generate Report</button>
<div id="report"></div>

<script>
  function getGrade(score) {
    if (score >= 90) return 'A';
    if (score >= 80) return 'B';
    if (score >= 70) return 'C';
    if (score >= 60) return 'D';
    return 'F';
  }

  function getFeedback(score) {
    if (score >= 90) return 'Outstanding performance!';
    if (score >= 70) return 'Good work — keep it up.';
    if (score >= 60) return 'Passing, but room to improve.';
    return 'Needs significant improvement.';
  }

  document.querySelector('#gen').addEventListener('click', () => {
    const name  = document.querySelector('#name-in').value.trim() || 'Student';
    const score = Number(document.querySelector('#score-in').value);
    const grade = getGrade(score);
    const date  = new Date().toLocaleDateString('en-GB', {
      day: 'numeric', month: 'long', year: 'numeric'
    });

    // Note: building HTML with template literals and CONTROLLED data (no user HTML input)
    const html = `
      <div class="card">
        <h2>Performance Report</h2>
        <p><strong>Student:</strong> ${name}</p>
        <p><strong>Date:</strong> ${date}</p>
        <p><strong>Score:</strong> ${score}/100</p>
        <p><strong>Grade:</strong> ${grade}</p>
        <p style="color:${score >= 60 ? 'green' : 'red'}">
          ${getFeedback(score)}
        </p>
      </div>
    `;
    document.querySelector('#report').innerHTML = html;
  });
</script>

5. Code Breakdown

Expressions inside interpolation

The template calls getGrade(score) and getFeedback(score) directly inside ${}. The ternary ${score >= 60 ? 'green' : 'red'} applies conditional styling inline.

Controlled data vs user input

The name variable comes from user input but is embedded in HTML via innerHTML here — this is a security risk in general. In production, use textContent for user-supplied values. The score, grade, and date are all generated by your code, so they are safe.

Fallback with ||

value.trim() || 'Student' uses the short-circuit OR: if the trimmed value is empty (falsy), it falls back to 'Student'. A safe default for optional inputs.

6. Common Mistakes

Mistake 1 — XSS with user input in template literals used as innerHTML

// Dangerous — user input goes directly into innerHTML
const userInput = '<img src=x onerror="alert(1)">';
container.innerHTML = `<p>${userInput}</p>`; // XSS attack!

// Safe — use textContent for user-controlled values
const p = document.createElement('p');
p.textContent = userInput; // treated as plain text, not HTML
container.appendChild(p);

Mistake 2 — Unnecessary complexity inside interpolation

// Hard to read — too much logic in the template
const msg = `${arr.filter(x => x.active).sort((a,b) => b.score-a.score).slice(0,3).map(x => x.name).join(', ')}`;

// Better — compute outside the template
const topNames = arr.filter(x => x.active)
  .sort((a, b) => b.score - a.score)
  .slice(0, 3)
  .map(x => x.name)
  .join(', ');
const msg2 = `Top performers: ${topNames}`;

Mistake 3 — Accidental whitespace in multi-line templates

// The indentation is part of the string
const html = `
    <p>Hello</p>
`;
// Starts with newline + 4 spaces — may cause layout issues
// Use .trim() to remove leading/trailing whitespace
const clean = html.trim();

7. Best Practices

  1. Use template literals whenever you embed variables or need multi-line strings.
  2. Keep interpolations simple — compute complex values in variables before the template.
  3. Never embed user input in innerHTML via template literals — always use textContent for user data.
  4. Use .trim() on multi-line templates when the leading/trailing newlines are unwanted.
  5. Use tagged templates for HTML escaping, internationalisation, or CSS-in-JS libraries.

8. Practice Exercise

  1. Rewrite five string concatenations you have written in earlier modules using template literals.
  2. Write a renderTable(headers, rows) function that builds an HTML table string using template literals and map.
  3. Write a simple tagged template highlight that wraps every interpolated value in <strong> tags.

9. Assignment

Build a "Receipt Generator" page.

  1. Allow the user to add items (name + price) to a list.
  2. When "Generate Receipt" is clicked, build a formatted receipt string using a template literal — include item list with line items, subtotal, tax (8%), and grand total.
  3. Display the receipt in a <pre> element (preserves whitespace/newlines).
  4. Add a "Print" button that calls window.print().
  5. All item names come from user input — use textContent for the item list, but the calculated numbers can go in the template directly.

Deliverable: One HTML file.

10. Interview Questions

  1. What is a template literal?
    A string literal using backticks that supports embedded expressions via ${}, multi-line content without escape characters, and tagged templates for custom processing.
  2. What can go inside ${}?
    Any JavaScript expression — variables, arithmetic, ternaries, function calls, method chains, even nested template literals. The result is converted to a string via toString().
  3. What is a tagged template?
    A function placed before the backtick that receives the template's string parts and interpolated values as separate arguments. Used in libraries like styled-components, graphql-tag, and for custom escaping.
  4. What is the security risk of using template literals with innerHTML?
    If user-supplied data is embedded in a template literal and the result is set as innerHTML, the browser parses it as HTML — potentially executing malicious scripts (XSS). Always use textContent for user input.

11. Additional Resources

  • MDN — Template literals
  • MDN — Tagged templates
  • javascript.info — Template literals