Putting an expression before a template literal turns it into a call: taga${x}b${y}c calls tag(strings, x, y).
- First argument:
strings, an array of the literal pieces. It always has one more element than there are substitutions. It is frozen, and so is itsstrings.rawarray. - Remaining arguments: the substitution values, not converted to strings. The tag decides what to do with them (
${obj}is not stringified for you). strings.raw: the source text of each piece, with escape sequences left unprocessed. For`a\n`the cooked element is"a"plus a newline; the raw element isa\nwith a literal backslash.- Return value: whatever the tag returns, which need not be a string. Libraries return DOM nodes, query objects, or functions.
The spec builds the strings array once per source location per realm (GetTemplateObject) and caches it, so the same call site passes the identical array each time it runs. That identity makes it a safe cache key. Two textually identical tagged templates at different locations get different arrays.
If a piece contains an invalid escape such as \unicode, an untagged template is a syntax error, but a tagged one is allowed: the cooked element is undefined and the raw text is still available.
String.raw is the built-in tag that returns the raw text with substitutions joined in. It is handy for Windows paths and for regex source.
Tagging is how safe-by-construction APIs are built: the tag sees the fixed text and the dynamic values separately, so it can escape or parameterize the values (HTML escaping, SQL placeholders). Plain template literals cannot do that, because they hand you only the merged string.