Skip to content
Carl Victor Fontanos
Carl Victor Fontanos

Carl Victor Fontanos

Software Engineer

I build web applications and share what I learn along the way.

© 2026

Tagged Template Literals: The Feature Behind Every html`` You've Seen

C
Carlo Fontanos
· 2 min read

Template literals everyone knows. But put a bare function name in front of the backtick and you've entered a different feature entirely:

function tag(strings, ...values) {
    console.log(strings);   // ["Hello ", ", you have ", " messages"]
    console.log(values);    // ["Anna", 5]
}

tag`Hello ${name}, you have ${count} messages`;

The function receives the static string parts and the interpolated values separately, before they're joined. That separation is the whole superpower: the tag decides how values become text.

The practical payoff: an auto-escaping html tag

Building HTML with plain template literals is an XSS bug waiting for user input. A tag function fixes it structurally - static parts pass through, dynamic parts get escaped, always:

function escapeHtml(value) {
    return String(value)
        .replaceAll('&', '&amp;').replaceAll('<', '&lt;')
        .replaceAll('>', '&gt;').replaceAll('"', '&quot;')
        .replaceAll("'", '&#39;');
}

function html(strings, ...values) {
    return strings.reduce((out, str, i) =>
        out + str + (i < values.length ? escapeHtml(values[i]) : ''), '');
}

const card = html`
${user.name}: ${user.message}
`;

Now <script> in a comment renders as harmless text, and nobody on the team has to remember to escape anything. The safe path became the only path - which is what good security code looks like.

You can extend it with an opt-out for pre-sanitized fragments (a raw() wrapper object the tag recognizes), which mirrors how the big template systems do it.

The raw strings bonus

The strings array carries a .raw property with backslashes unprocessed, and String.raw is a built-in tag exposing it. It's the sane way to write Windows paths and regex sources:

String.raw`C:\Users\node_modules`;   // backslashes survive
new RegExp(String.raw`\d+\.\d{2}`);  // no double-escaping

Where you've already met this feature

lit-html's rendering, styled-components' CSS blocks, many SQL clients' safe query builders (values become bound parameters, not concatenated strings) - all tagged templates. The pattern generalizes: whenever "static template + untrusted values" appear together, a tag function can enforce the safety rule at the syntax level.

Server-side folks: this is the same philosophy as prepared statements, and the same reason security-sensitive comparisons get dedicated functions. Make the correct thing the effortless thing.

C
Written by Carlo Fontanos

Full-stack web developer sharing practical tutorials and building tools that ship.

Got something on your mind?

My inbox is open - no forms disappearing into the void here.

  • Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
  • Hire me for a project Have something custom in mind? Let's talk scope and timelines.
  • Product support Bought something here? I'll help you get it running.

I usually reply within 1-2 business days.

Message sent!

Your details are only used to reply to you.

Keep reading