skip to content

Template Literals and Tagged Templates

Template literals look like syntax sugar until you meet the tagged form, where a function receives the static chunks and the interpolated values separately. That hook is how libraries build escaped HTML, SQL fragments, and styled components.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

questions

5

In JavaScript, what is a template literal (a string written in backticks), and how does it differ from building the same string with the + operator?

level: juniorimportance: must knowfreq 80%

answer

  1. Backticks, not quotes
  2. Any expression goes in the slot
  3. Newlines and indentation are literal
  4. Values pass through string conversion
  5. Symbols throw; objects give [object Object]

basics

~20 s

A template literal is a backtick-delimited string that embeds any expression with ${...} and keeps newlines exactly as typed. Interpolated values are converted to strings automatically, so it produces what + would for most inputs, with far less quote juggling.

solid answer

~50 s

A template literal is written in backticks instead of quotes. Inside it, `${ ... }` takes any *expression* — not just a variable — evaluates it, converts the result to a string, and splices it in. Newlines and leading indentation typed inside the backticks are part of the string, so multi-line text needs no `\n` and no concatenation. Single and double quotes need no escaping; a backtick, a backslash, or a literal `${` does. Against `+`, the everyday win is readability — you read the shape of the output instead of a chain of operands. The conversion is the ordinary one: an array becomes its comma-joined elements, a plain object becomes `[object Object]`, `null` becomes `"null"`. So interpolation is not a formatter and provides no escaping or safety of any kind. Templates also nest — a `${}` slot may hold another backtick string.

code

javascript · 11 lines
javascript
const user = { name: 'Ada', roles: ['admin', 'dev'] };

console.log(`${user.name} has ${user.roles.length} roles: ${user.roles}`);
// Ada has 2 roles: admin,dev

console.log(`object: ${user}`);        // object: [object Object]
console.log(`missing: ${user.email}`); // missing: undefined

const indented = `first
    second`;
console.log(JSON.stringify(indented)); // "first\n    second"

go deeper

for a junior

Be able to write one from memory: backticks, ${expression}, real newlines, and no escaping needed for quotes. Say plainly that non-strings are converted to text automatically.

for a middle

Explain the conversion rule concretely — arrays comma-join, plain objects become [object Object], null and undefined print as words, symbols throw — and note that indentation inside the literal is part of the value.

for a senior

Show the production judgment: interpolation is not escaping, so templates that build markup or queries need a tagged form or a real escaping step, and whitespace-sensitive output needs deliberate dedenting.

for a principal

Own the convention: where templates are the house string-building tool, decide which contexts must never use a bare template and provide the safe tagged helper, so correctness does not depend on each author remembering.

## The syntax A template literal is a string literal delimited by backticks (`` ` ``) instead of `'` or `"`. It arrived in ES2015 and adds two things ordinary string literals lack: **interpolation** and **literal newlines**. ```js const name = 'Ada'; const items = ['a', 'b']; const msg = `Hi ${name}, you have ${items.length} item(s).`; // 'Hi Ada, you have 2 item(s).' ``` Everything between `${` and its matching `}` is an **expression**, not merely a variable name. Function calls, ternaries, arithmetic, property access, even a nested template all work: ```js const n = 3; `You have ${n} ${n === 1 ? 'message' : 'messages'}${n > 0 ? ` (${n} unread)` : ''}`; ``` Evaluation is eager and left to right, top to bottom: every placeholder is evaluated once, at the moment the literal is evaluated, and the pieces are concatenated. There is no lazy or deferred behaviour — a template literal is an expression that produces a `string` and nothing more. ## Multi-line and whitespace A newline typed between backticks is a real newline in the value, and so is the indentation before it. This is the most common surprise: ```js function render() { return `line one line two`; } // 'line one\n line two' <- four spaces survive ``` Nothing dedents for you. If the surrounding code indentation must not leak into the output, either write the template flush against the left margin, strip it afterwards, or build it from an array and `join('\n')`. ## Escaping inside a template The usual backslash escapes still work (`\n`, `\t`, `\u{1F600}`). Three characters need escaping that quotes did not: a backtick (`` \` ``), a backslash (`\\`), and a literal dollar-brace sequence (`\${`) when you want the text `${` rather than a placeholder. Conversely, `'` and `"` are ordinary characters inside a template, which is why templates are pleasant for HTML-ish or quote-heavy text. ## The conversion rule Each interpolated value goes through the language's string conversion (the spec's ToString), the same operation `String(value)` performs. Practical consequences: - `` `${[1, 2]}` `` is `'1,2'` — arrays join with commas, and an empty slot or `null` element becomes empty text. - `` `${{ a: 1 }}` `` is `'[object Object]'` — a plain object's default `toString`. Interpolation never inspects an object's contents the way a debugger view does; reach for `JSON.stringify` when you want the shape. - `` `${null}` `` is `'null'` and `` `${undefined}` `` is `'undefined'` — both become visible text, which is why a missing field shows up as the word "undefined" in a rendered string. - A symbol throws: `` `${Symbol('id')}` `` raises a `TypeError`, because string conversion refuses symbols. `String(sym)` is the one conversion that is allowed to produce `'Symbol(id)'`. There is one narrow divergence from `+`: interpolation asks an object for a string first, while `+` asks it for its default primitive. An object that defines both `valueOf` and `toString` can therefore stringify differently in the two forms: ```js const weird = { valueOf: () => 1, toString: () => 'S' }; '' + weird; // '1' `${weird}`; // 'S' ``` For ordinary values — strings, numbers, booleans, arrays, plain objects — the two agree. ## What it deliberately does not do Interpolation performs **no escaping**. A template literal that builds markup, a shell command, or a query splices attacker-controlled text in verbatim; the backticks add nothing defensive. The language's answer to that is the tagged form, where a function receives the static chunks and the values separately and can transform the values. A plain template literal is pure convenience. It also does not format: no padding, no locale-aware numbers or dates, no precision control. Those come from methods on the value or from the `Intl` APIs, applied inside the placeholder. ## When to prefer which Use a template literal for anything with more than one moving part, for multi-line text, and for strings full of quotes. Plain `+` still reads fine for a single concatenation (`prefix + id`). Performance is not a deciding factor: engines compile both to comparable work, so choose on readability. One genuine caveat is that a long multi-line template inside indented code carries that indentation, so templates used for whitespace-sensitive output (YAML, Python-like text, some Markdown) need care.

  • What does a template literal do with an object that has no custom toString?
    It produces `'[object Object]'`, the default `Object.prototype.toString` result — interpolation converts to a string, it does not inspect structure. If you want the contents, call `JSON.stringify(obj)` inside the placeholder, and remember that stringify drops functions and `undefined` properties.
  • Is there any value that a template literal refuses to interpolate?
    Yes — a symbol. `` `${Symbol('id')}` `` throws a `TypeError`, because the language's string conversion rejects symbols to stop them leaking into text accidentally. The explicit `String(sym)` call is special-cased and returns `'Symbol(id)'`, so wrap the symbol if you actually want it printed.
  • How do you write a template that must contain the literal characters ${ without starting a placeholder?
    Escape the dollar sign: `` `costs \${amount}` `` yields the text `costs ${amount}`. Backticks and backslashes need the same treatment inside a template (`` \` `` and `\\`), while single and double quotes are ordinary characters and need none.

saying these in an interview costs you the question

  • Says only variables, not expressions, fit inside ${}
  • Thinks leading indentation in a multi-line template is stripped
  • Believes template literals escape HTML or make strings safe
  • Expects ${obj} to print the object's properties
  • Claims template literals are slower so should be avoided

context

open as a page

When you call a JavaScript tagged template such as tag`a${x}b`, what arguments does the tag function actually receive, and what is the relationship between them?

level: middleimportance: must knowfreq 52%

basics

~20 s

The tag function receives the static text chunks as its first argument — an array with a raw property holding the unescaped text — followed by one argument per interpolated value. The chunks array always has exactly one more element than the number of values.

open as a page

A tagged template can escape interpolated values while a plain template literal cannot. Why is that structurally true, and what does an escaping tag still fail to protect you from?

level: seniorimportance: should knowfreq 38%

basics

~20 s

A tag receives the author-written text and the runtime values as separate arguments, so it can transform only the values — a plain template has already concatenated them and cannot tell them apart. The remaining gap is context: escaping that is right inside element text is wrong inside an attribute, a URL, or a script.

open as a page

What does JavaScript's built-in String.raw tag do to a template literal, and what is it used for?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

String.raw is a built-in tag function that returns the template's text with backslash escape sequences left uninterpreted, so String.rawa\nb is four characters, not three. It exists for text where a backslash means a backslash: Windows paths, regex sources, and generated code.

open as a page

A JavaScript tagged template sits inside a function that is called many times. What can the tag function assume about the identity of the strings array it receives on each call, and how do libraries exploit that?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

Each tagged-template call site has one template object, reused on every execution, and it is frozen. A tag can therefore use that array as a stable cache key — typically in a WeakMap — to parse or compile the static text once per site rather than once per call.

open as a page