ESC

Type to search the knowledge base.

Template Literals and Tagged Templates

Backtick strings, interpolation, multiline, raw strings, and tagged templates for DSLs — without inventing XSS.

beginner3 min read
  • javascript
  • template-literals

Template literals replace most 'a' + b + 'c' mess with readable interpolation and real multiline strings. Tagged templates pass the parts to a function — the foundation for HTML DSLs, SQL helpers, and i18n libraries.

Basics

const user = 'Ada';
const msg = `Hello, ${user}!`;

const block = `
  line 1
  line 2
`; // includes newlines and indentation as written

Expressions inside ${} can be any JS; keep them simple for readability.

`Total: ${items.reduce((s, i) => s + i.price, 0).toFixed(2)}`;

Nesting and escaping

const outer = `say ${`hello ${user}`}`;
// backticks inside: \`
const path = `C:\\temp\\file`; // still need escapes for \ and ` and ${

To include a literal ${, escape: \${not interpolated}.

String.raw

String.raw`C:\temp\file`; // backslashes preserved
// useful for regex authoring helpers / Windows paths in demos

Also available as the default tag: String.raw is itself a tag function.

Tagged templates

function tag(strings, ...values) {
  console.log(strings); // TemplateStringsArray
  console.log(values);
  return strings.reduce((s, part, i) => s + part + (values[i] ?? ''), '');
}

tag`a${1}b${2}c`; // strings ['a','b','c'], values [1,2]

strings.raw holds unescaped escapes (like String.raw).

Practical tags

// logging
function debug(strings, ...values) {
  return strings.reduce((out, s, i) => {
    const v = values[i];
    const shown = typeof v === 'object' ? JSON.stringify(v) : v;
    return out + s + (shown ?? '');
  }, '');
}
console.log(debug`user=${user} count=${n}`);

For HTML, escape values by default — see tagged template sanitization on this site. Never do:

el.innerHTML = `<div>${untrusted}</div>`; // XSS

Performance myth

Template literals are not magically slow; engines optimize them. Don’t micro-optimize back to concat without data. Prefer clarity.

Interview answer (out loud)

“Template literals use backticks, allow ${interpolation} and multiline strings. A tag function before the literal receives the string chunks and values separately — useful for safe HTML, i18n, and DSLs. I still escape untrusted values before any HTML sink.”

Multiline indentation trick

function dedent(strings, ...values) {
  // simple: strip common leading whitespace from cooked strings
  let raw = strings.reduce((out, s, i) => out + s + (values[i] ?? ''), '');
  const lines = raw.replace(/^\n/, '').replace(/\n\s*$/, '').split('\n');
  const indents = lines.filter(Boolean).map((l) => l.match(/^ */)[0].length);
  const min = Math.min(...indents);
  return lines.map((l) => l.slice(min)).join('\n');
}

const sql = dedent`
  select id, name
  from users
  where active = 1
`;

Real dedent libraries handle edge cases; the point is tags enable this class of DX.

Expressions and side effects

`value=${(console.log('runs'), 42)}`; // log runs while building string

Interpolation is eager. Don’t hide network calls inside ${} — keep templates for formatting values you already have.

When concat is clearer

Very short '(' + id + ')' can read better than a template. Prefer templates when there are multiple interpolations or newlines — not as ideology.

Further reading

Related guides