Tagged Templates
This lesson explains Tagged Templates with clear examples, use cases, and best practices so you can use modern JavaScript confidently in real projects.
ES6+ Tagged Templates Overview
Tagged templates are an advanced form of template literals where a
function processes the string before it is returned. Instead of
writing `Hello`, you write
tag`Hello`, and the tag function receives the string
parts and interpolated values.
Tagged templates are useful for custom formatting, HTML escaping,
internationalization, styled component libraries, SQL query
builders, and any scenario where template output needs special
processing.
| Feature | Description |
| Introduced In | ES6 (ECMAScript 2015) |
| Syntax | tagFunction`text ${value}` |
| Tag Function Receives | String parts and interpolated values |
| Return Value | Any value, often a processed string |
| Raw Strings | strings.raw |
| Common Uses | Escaping, i18n, styling, custom DSLs |
Basic Tagged Template Example
function highlight(
strings,
...values
) {
return strings.reduce(
(result, part, index) =>
result +
part +
(values[index] !== undefined
? `[[${values[index]}]]`
: ""),
""
);
}
const name = "Alex";
const role = "Admin";
const message =
highlight`User ${name} has role ${role}.`;
console.log(message);
The tag function receives the static string parts as an array and
the interpolated values as separate arguments. It can transform
the final output however you want.
How Tagged Templates Work
When JavaScript sees a tagged template literal, it first splits
the template into string segments and expression values, then
calls the tag function with those parts.
| Step | Description | Example |
| Write Template | Place a function name before backticks. | tag`Hello ${name}` |
| Split Template | Engine separates static and dynamic parts. | ["Hello ", ""] and name |
| Call Tag Function | Pass strings array and values. | tag(strings, name) |
| Process Output | Transform, escape, or format result. | Custom logic inside tag |
| Return Value | Tag function returns final result. | String, object, or other value |
Understand Tag Function Arguments
function inspect(
strings,
...values
) {
console.log("Strings:", strings);
console.log("Values:", values);
return strings.join("---");
}
const city = "Paris";
inspect`Welcome to ${city}!`;
For tag`Welcome to ${city}!`, the
strings array contains "Welcome to " and
"!", while values contains
"Paris".
Use Raw Template Strings
function showRaw(strings) {
console.log(strings[0]);
console.log(strings.raw[0]);
}
showRaw`Line 1\nLine 2`;
const path =
String.raw`C:\projects\app\src`;
console.log(path);
Tagged template string arrays include a raw property
with unprocessed escape sequences. String.raw is a
built-in tag for keeping backslashes unchanged.
Escape HTML with a Tag Function
function escapeHtml(value) {
return String(value)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function html(
strings,
...values
) {
return strings.reduce(
(result, part, index) =>
result +
part +
(values[index] !== undefined
? escapeHtml(values[index])
: ""),
""
);
}
const userInput = "<script>alert('xss')</script>";
const safeHtml =
html`<p>Comment: ${userInput}</p>`;
console.log(safeHtml);
A common tagged template pattern is automatic escaping of dynamic
values before inserting them into HTML or other sensitive output.
Styled Output Tag Example
function css(
strings,
...values
) {
return {
className: "theme-card",
styles: strings.reduce(
(result, part, index) =>
result +
part +
(values[index] ?? ""),
""
)
};
}
const primaryColor = "#2563eb";
const cardStyles =
css`
.card {
color: ${primaryColor};
padding: 16px;
}
`;
console.log(cardStyles);
Tagged templates can return objects instead of plain strings. This
pattern is similar to how styled-component libraries process CSS
templates in modern frontend development.
Internationalization Tag Example
const translations = {
en: {
greeting: "Hello, USER!"
},
fr: {
greeting: "Bonjour, USER!"
}
};
function i18n(lang) {
return function (
strings,
...values
) {
const template =
translations[lang].greeting;
return template.replace(
"USER",
values[0]
);
};
}
const t = i18n("fr");
const message = t`${"Alex"}`;
console.log(message);
Tag functions can power translation systems by choosing the
correct language template and inserting dynamic values safely.
Custom Formatting Tag
function currency(
strings,
...values
) {
const formattedValues =
values.map((value) =>
`${Number(value).toFixed(2)}`
);
return strings.reduce(
(result, part, index) =>
result +
part +
(formattedValues[index] ?? ""),
""
);
}
const subtotal = 19.5;
const tax = 1.75;
const receipt =
currency`Subtotal: ${subtotal} | Tax: ${tax}`;
console.log(receipt);
Tagged templates are useful when the same formatting rule should
apply automatically to every interpolated value in a template.
Tagged Templates vs Regular Template Literals
| Feature | Tagged Template | Regular Template Literal |
| Syntax | tag`text` | `text` |
| Processing | Custom tag function runs first. | Direct string interpolation only. |
| Return Type | Any value. | Always a string. |
| Best For | Escaping, DSLs, styling, i18n. | Simple dynamic strings. |
Common Tag Function Patterns
| Pattern | Description | Example Use |
| Reducer Pattern | Combine strings and values with reduce(). | HTML escaping, formatting. |
| Curried Tag | Return a tag function from another function. | Language or theme selection. |
| Object Return | Return structured data instead of text. | CSS-in-JS libraries. |
| Raw Access | Use strings.raw for unescaped text. | File paths and regex patterns. |
Common Tagged Template Use Cases
- Escaping HTML and preventing XSS in dynamic templates.
- Building styled component and CSS-in-JS systems.
- Creating translation and localization helpers.
- Formatting numbers, dates, and currency automatically.
- Constructing SQL or query DSLs with safer interpolation.
- Generating markdown or custom markup languages.
Tagged Template Best Practices
- Use tagged templates when every interpolated value needs the same processing.
- Keep tag functions small, pure, and easy to test.
- Escape or validate untrusted values inside the tag function.
- Prefer regular template literals for simple one-off strings.
- Document what your custom tag returns and expects.
- Use curried tags when configuration such as language or theme is needed.
- Handle missing values safely when combining strings and values.
Common Tagged Template Mistakes
- Assuming tagged templates automatically sanitize HTML.
- Forgetting that the tag function must rebuild the final string manually.
- Not handling undefined interpolated values in reduce logic.
- Using tagged templates for simple cases that only need normal templates.
- Returning inconsistent value types from the same tag function.
- Ignoring
strings.raw when escape sequences must stay literal. - Building overly complex tag logic that belongs in normal functions.
Reusable Tag Helper
function joinTemplate(
strings,
...values
) {
return strings.reduce(
(result, part, index) => {
const value =
values[index];
return (
result +
part +
(value === undefined ? "" : value)
);
},
""
);
}
function bold(
strings,
...values
) {
return `**${joinTemplate(strings, ...values)}**`;
}
const product = "Keyboard";
console.log(
bold`Featured product: ${product}`
);
Reusable helper functions make custom tags easier to maintain,
especially when several tags share the same string-joining logic.
Key Takeaways
- Tagged templates call a function before returning template literal output.
- The tag function receives string parts and interpolated values separately.
- They enable custom escaping, formatting, styling, and DSL behavior.
strings.raw and String.raw preserve unescaped text. - Use them when template processing should be automatic and reusable.
Pro Tip
Reach for tagged templates when the template itself needs a policy,
such as escaping, translation, or formatting. If you only need to
insert a variable, a regular template literal is enough.