跪拜 Guibai
← All articles
Frontend · JavaScript

Tagged Templates: JavaScript's String Pipeline Beyond Concatenation

By vipxieliang ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Any codebase that renders user input through `innerHTML` can prevent XSS by separating trusted static fragments from untrusted interpolations at the language level. Understanding tagged templates also demystifies styled-components and lit-html, so debugging and extending those libraries stops being black-box work.

Summary

Template literals already fix the pain points of `+` concatenation: backticks give you multi-line strings and `${}` accepts any expression. The step up comes from putting a function name before a backtick string. That tag function receives two arrays: static fragments and evaluated values, with `strings.length === values.length + 1` always true. The tag decides exactly how to assemble the final output, which turns simple interpolation into a programmable string processor.

That mechanism is what powers styled-components, lit-html, and GraphQL's gql tag. A minimal `safeHtml` tag escapes only the values and leaves static fragments untouched, blocking XSS when user input reaches `innerHTML`. The same pattern drives a simple i18n tag and a regex-based syntax highlighter. Any transformation you want on interpolated content, from uppercasing to localization, can be encoded as a tag.

Practical mastery comes down to two details: `strings.raw` exposes unescaped source text, and the canonical tag function uses `reduce` with the length invariant to rebuild strings. For production escaping, mature libraries like DOMPurify and framework built-in escapes are safer than hand-rolled regex.

Takeaways
— Template literals support multi-line strings and `${}` expressions, replacing `+` concatenation and `\n` escapes.
— A tagged template invokes a function with `strings` (static fragments) and `values` (interpolation results), and `strings.length === values.length + 1` always holds.
— `strings.raw` returns the original, unescaped text, useful for code highlighting and template engines.
— The canonical tagged-template pattern uses `reduce` to rebuild the string while transforming values.
— A `safeHtml` tag escapes only `values`, keeping trusted `strings` intact, which blocks XSS in `innerHTML`.
— Production code should use mature libraries like DOMPurify or framework built-in escaping rather than a hand-written tag.
— i18n tags can select language-specific templates and fill placeholders from `values`, often combined with `Intl` for locale formatting.
— styled-components, lit-html, and GraphQL's gql all build their DSLs on tagged templates.
Conclusions

The trust split between static fragments and interpolated values is the core security benefit; it gives developers a language-level mechanism to enforce escaping at the point of construction rather than relying on framework conventions.

Popular libraries like styled-components and lit-html are not magic; they rely on a built-in language feature any JavaScript developer can learn in an afternoon, which lowers the barrier to extending or debugging them.

The article's minimal highlighter shows that even a simple regex-based tag can produce a usable tool, which hints at why tagged templates became the syntax of choice for a wide range of string-processing DSLs.

Concepts & terms
Template literal
A JavaScript string literal wrapped in backticks, supporting multi-line content and `${}` interpolation.
Tagged template
A template literal preceded by a function name. The function receives an array of static string fragments and an array of interpolated values, letting it customize the final output.
strings.raw
A property on the static fragments array that returns the original text without escape sequences processed, useful for preserving source formatting.
XSS escaping
Replacing HTML-special characters like `<`, `>`, `&`, `"`, and `'` with entity references so user input is rendered as text rather than executable markup.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗