Skip to main content

Template literals in JavaScript

Template literals are strings wrapped in backticks (`) that let you embed expressions with ${} syntax, write multi-line strings without escape sequences, and use tagged templates for advanced processing. Introduced in ES6.

Theory

TL;DR

  • Template literals work like mail merge: one template, inject data where needed
  • Main difference: backticks + ${} vs concatenation with +
  • Use them whenever a string has variables, multi-line content, or dynamic values
  • Static strings with no variables? Regular quotes are clearer there
  • Inside ${} you can use any expression: math, function calls, ternary operators

Quick example

javascript
const name = "Alice"; const age = 28; // Old way - concatenation const old = "Hello, " + name + "! You are " + age + " years old."; // Template literal const modern = `Hello, ${name}! You are ${age} years old.`; // "Hello, Alice! You are 28 years old." // Multi-line - no \n needed const message = `Welcome, ${name}! Your account is active. Age: ${age}`;

The backtick syntax removes the chain of + operators between every variable and string piece.

Key difference

Template literals evaluate expressions inside ${} at runtime and convert results to strings automatically. With concatenation, you chain pieces with + and need \n for line breaks. Template literals handle both. They also unlock tagged templates, where a function receives the string parts and expression values separately before anything is concatenated. Regular strings cannot do that.

When to use

  • Any string that mixes text with variables
  • Multi-line content: HTML snippets, SQL queries, error messages
  • Complex expressions: ${user.isAdmin ? 'admin' : 'user'}
  • Dynamic attributes and class names in components
  • Avoid for plain static strings with no dynamic parts - regular quotes read more clearly there

In practice, once you switch to template literals for dynamic strings, concatenation feels clunky even for simple two-part cases.

How it works internally

When the JS engine hits a template literal, it parses the backtick-delimited string and locates each ${} block. Each expression gets evaluated in the current scope, converted via .toString(), and inserted at that position. For tagged templates, the engine splits the static string parts and evaluated expression values into separate arguments, then passes them to the tag function before any string building happens. Structure is resolved at parse time; expression evaluation happens at runtime.

Common mistakes

Forgetting the $ in ${}

javascript
const name = "Alice"; // ❌ Prints literal curly braces const greeting = `Hello, {name}!`; console.log(greeting); // "Hello, {name}!" - not what you wanted // ✅ Correct const greeting2 = `Hello, ${name}!`; console.log(greeting2); // "Hello, Alice!"

Template literals as object keys

javascript
const key = "user"; // ❌ The key becomes the literal string "${key}_name" const bad = { [`${key}_name`]: "Alice" }; // needs square brackets // Without brackets: { '${key}_name': 'Alice' } - wrong // ✅ Computed property syntax with square brackets const obj = { [`${key}_name`]: "Alice" }; console.log(obj); // { user_name: 'Alice' }

Unescaped user input (XSS)

javascript
const userInput = "<img src=x onerror='alert(1)'>"; // ❌ The script executes when rendered const html = `<div>${userInput}</div>`; // ✅ Escape before injecting function escapeHtml(text) { const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }; return text.replace(/[&<>"']/g, m => map[m]); } const safeHtml = `<div>${escapeHtml(userInput)}</div>`; // <div>&lt;img src=x onerror='alert(1)'&gt;</div> - safe

Unexpected whitespace in multi-line templates

javascript
// ❌ Includes a leading newline and indentation spaces const html = ` <div> <p>Content</p> </div> `; // ✅ Use .trim() or skip the leading newline const html2 = `<div> <p>Content</p> </div>`.trim();

Real-world usage

  • React: dynamic className strings and aria-label attributes
  • Express/Node.js: SQL query strings, log messages, error responses
  • Apollo Client: GraphQL queries via the gql tagged template
  • styled-components: CSS-in-JS with tagged template syntax
  • Jest: test descriptions and snapshot strings

Follow-up questions

Q: What happens if you put an object inside ${}?
A: It calls .toString() on the value, which gives [object Object]. Use JSON.stringify(obj) explicitly if you need the actual content.

Q: Can you use template literals as object property names?
A: Not directly. Without square brackets the backtick string is treated as a literal key. You need computed property syntax: { [\key_${x}`]: value }`.

Q: How do tagged templates actually work?
A: The tag is a function that receives the static string parts as an array and the evaluated expression values as separate arguments: (strings, ...values). The function decides how to combine them. That is how SQL tag functions escape query parameters and how styled-components processes CSS strings.

Q: Are template literals slower than concatenation?
A: No. Modern engines like V8 and SpiderMonkey optimize both. The performance difference is negligible. The reason to prefer template literals is readability.

Q: (Senior) How do you build a tagged template that automatically prevents XSS?
A: Write a tag function that maps over the values array and escapes each one before insertion. It receives (strings, ...values) - escape each value and interleave with the strings parts to build the final string. This is exactly how lit-html and htm handle user-provided content.

Examples

Basic interpolation and multi-line string

javascript
const user = { name: "Alice", email: "alice@example.com" }; const isActive = true; const card = ` Name: ${user.name} Email: ${user.email} Status: ${isActive ? "active" : "inactive"} Joined: ${new Date().toLocaleDateString()} `.trim(); console.log(card); // Name: Alice // Email: alice@example.com // Status: active // Joined: 1/15/2025

Ternary operators, method calls, and property access all work inside ${}. The .trim() removes the leading and trailing newlines that come from the backtick wrapping.

Tagged template for SQL query safety

javascript
function sql(strings, ...values) { const escaped = values.map(v => typeof v === "string" ? `'${v.replace(/'/g, "''")}'` : v ); let result = strings[0]; for (let i = 0; i < escaped.length; i++) { result += escaped[i] + strings[i + 1]; } return result; } const userId = 42; const userName = "O'Brien"; // contains an apostrophe const query = sql`SELECT * FROM users WHERE id = ${userId} AND name = ${userName}`; console.log(query); // SELECT * FROM users WHERE id = 42 AND name = 'O''Brien' // The apostrophe is escaped - SQL injection blocked

The tag function runs before the string is built. It receives the static parts in strings and the evaluated expressions in values. This is the same pattern Apollo Client uses for GraphQL queries with the gql tag.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?