
Tagged Template Literals in JavaScript

Most developers meet template literals through the obvious features first:
- string interpolation
- multi‑line strings
That alone makes them useful. Yet ES6 also added a more powerful idea that tends to stay slightly mysterious for much longer: tagged template literals.
The name sounds more exotic than the feature really is.
A tagged template literal is just a template literal that is passed through a function before JavaScript turns it into a normal string. That function gets direct access to the static string parts and the interpolated values separately.
Once you see that clearly, the feature stops feeling magical and starts feeling practical.
A Normal Template Literal Evaluates to a String
This is the version most people already know:
const name = 'Ellie';const message = `Hello, ${name}!`;console.log(message);That becomes the string Hello, Ellie!.
The interpolation happens automatically and the final result is just a string value.
A Tagged Template Literal Calls a Function Instead
If we put a function name before the template literal, JavaScript calls that function:
const logTemplate = ( strings: TemplateStringsArray, ...values: unknown[]): string => { console.log(strings); console.log(values); return 'done';};const result = logTemplate`Hello, ${'Ellie'}!`;Now logTemplate receives:
- an array of the literal string chunks
- the interpolated values as separate arguments
For the example above, the rough shape is:
strings:['Hello, ', '!']values:['Ellie']
That separation is the whole point.
Why This is Interesting
With an ordinary template literal, interpolation happens immediately and produces a plain string.
With a tagged template literal, we get a chance to inspect or transform the values first.
That means we can:
- escape unsafe characters
- format values consistently
- build domain‑specific mini‑languages
- preserve raw string fragments
- reject or alter certain interpolations
This is why tagged templates show up in libraries such as CSS-in-JS tools and GraphQL helpers. The syntax reads naturally, but the underlying function still has full control.
A Simple Formatting Example
Suppose we want a small helper that wraps interpolated values in brackets for debugging:
const debug = ( strings: TemplateStringsArray, ...values: unknown[]): string => { return strings.reduce((result, stringPart, index) => { const value = index < values.length ? `[${String(values[index])}]` : ''; return result + stringPart + value; }, '');};const user = 'Ellie';const count = 3;console.log(debug`User ${user} has ${count} unread messages.`);That produces:
User [Ellie] has [3] unread messages.The important part is not the brackets. It is that the tag function decides how interpolated values are handled.
Escaping is a More Realistic Use Case
One practical example is HTML escaping.
If we are generating markup as a string, raw interpolation can be dangerous:
const unsafeName = '<script>alert("x")</script>';const html = `<p>${unsafeName}</p>`;That string now contains the raw angle brackets.
With a tag function, we can escape the interpolated values before combining everything:
const escapeHtml = (value: string): string => value .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", ''');const safeHtml = ( strings: TemplateStringsArray, ...values: unknown[]): string => { return strings.reduce((result, stringPart, index) => { const value = index < values.length ? escapeHtml(String(values[index])) : ''; return result + stringPart + value; }, '');};const unsafeName = '<script>alert("x")</script>';const output = safeHtml`<p>${unsafeName}</p>`;Now the inserted value is escaped before it reaches the final string.
This is Useful, but It is Not Automatic Sanitisation
That caveat matters.
Tagged template literals do not make strings safe by themselves. The safety comes from what the tag function actually does.
If the tag function simply concatenates everything without escaping, nothing has been improved.
So the lesson is not "tagged templates are secure". The lesson is "tagged templates give you a clean place to enforce a rule".
The Raw Strings are Available Too
Template literals process escape sequences by default, but tag functions can also access the raw string fragments through strings.raw.
That becomes useful when the exact source text matters, for example with paths, regular‑expression‑like patterns, or other custom parsing situations.
const inspectRaw = ( strings: TemplateStringsArray, ...values: unknown[]): string => { console.log(strings[0]); console.log(strings.raw[0]); return '';};inspectRaw`Line one\nLine two`;The cooked string and raw string are not the same thing. That is part of what makes tagged templates flexible enough for library authors.
Most Application Code Does Not Need Them Everywhere
This is where restraint matters.
Tagged template literals are not a better replacement for every ordinary string. If all we need is:
const message = `Hello, ${name}`;then a plain template literal is already the right tool.
The tag becomes worthwhile when we need policy as well as formatting:
- consistent escaping
- structured transformation
- validation
- custom parsing rules
Without that extra job to do, a tag function is just abstraction for its own sake.
Reading the Function Signature is the Key
Once people understand the function arguments, the feature becomes much less intimidating.
The tag function receives:
- the literal string pieces
- each interpolated value separately
After that, it can return whatever it likes. Often that is a string, but it does not have to be. A tag function can return an object, an array, or some other structured result if that suits the problem.
That is why the feature shows up in API design as well as string formatting.
Tagged Templates are Really About Control
Plain template literals are convenient because they make strings easier to write.
Tagged template literals go one step further. They make interpolation programmable.
That is the real mental model to keep hold of. The syntax looks like string creation, but the behaviour is function‑driven. Once you see it that way, the feature stops looking like obscure language trivia and starts looking like a neat way to centralise formatting and escaping rules.
You probably will not use template tags every day. Still, when you need to intercept interpolation cleanly, they are one of the nicest ideas ES6 introduced.
Related Articles

Escaping and Unescaping Special Characters in JavaScript. 
Template Literals in JavaScript: Writing Multi‑Line Strings. Template Literals in JavaScript: Writing Multi‑Line Strings

Prepending PHP to a Page in Gatsby. Prepending PHP to a Page in Gatsby

Ethical Web Development ‑ Part I. Ethical Web Development ‑ Part I

Top Reasons to Work with a Local Web Developer in Brighton. Top Reasons to Work with a Local Web Developer in Brighton

How to Amend Git Commits. How to Amend Git Commits

The arguments Object vs. Rest Parameters in JavaScript. The
argumentsObject vs. Rest Parameters in JavaScript
Generate Parentheses in TypeScript: A Clean Backtracking Walkthrough. Generate Parentheses in TypeScript: A Clean Backtracking Walkthrough

Improve Page Performance with content‑visibility. Improve Page Performance with
content‑visibility
Enhancing User Experience with CSS and JavaScript Animations. Enhancing User Experience with CSS and JavaScript Animations

Prefix Sums and Hash Maps: Solving 'Subarray Sum Equals K'. Prefix Sums and Hash Maps: Solving 'Subarray Sum Equals K'

What is CORS and Why is My JavaScript fetch Blocked? What is CORS and Why is My JavaScript
fetchBlocked?