Chapter 79: Dynamic HTML Generation

Dynamic HTML with Tagged Template Literals

Implementing declarative, safe, and expressive HTML templating engines in vanilla JavaScript using ES6 tagged template literals.

LEARNING OBJECTIVES
  • Understand the mechanics of ES6 Tagged Template Literals (strings array, ...values arguments, raw strings).
  • Build a custom, zero-dependency html tag function that automatically escapes untrusted interpolations to prevent DOM XSS.
  • Support nested array interpolation and raw/safe HTML bypassing mechanics.
  • Compare tagged template rendering models with classic string concatenation and modern reactive libraries (e.g., Lit, HyperHTML).
🎬 INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
🌐
1. Input
Directives & Tags
⚙️
2. Parse
Tokenizer & AST
🌳
3. Layout
Box Model & Flow
🎨
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

📖 The Mental Model & Story (Intuitive Foundation)

Imagine writing an executive contract. The document has static boilerplate text (the legal terms that never change) and blank spaces where dynamic variables (client name, financial figures, address) are filled in.

+--------------------------------------------------------------------------------+
| STATIC BOILERPLATE (Strings Array):                                           |
| strings[0] = "<div><h3>"                                                      |
| strings[1] = "</h3><p>"                                                       |
| strings[2] = "</p></div>"                                                     |
+--------------------------------------------------------------------------------+
                                       +
+--------------------------------------------------------------------------------+
| DYNAMIC DATA VALUES (Values Array):                                           |
| values[0] = user.name (e.g. "<script>malicious()</script>")                   |
| values[1] = user.bio  (e.g. "Senior Architect")                              |
+--------------------------------------------------------------------------------+
                                       |
                     TAGGED TEMPLATE FUNCTION (The Notary)
                                       |
                                       v
[ Checks each dynamic value, sanitizes malicious characters, and merges safely ]

If you use standard string concatenation ("<div>" + user.name + "</div>"), the browser cannot distinguish between the author's trusted HTML markup and the untrusted user input. A malicious user entering <img src=x onerror=stealCookies()> becomes parsed as executable HTML.

A Tagged Template Literal acts like an automated legal notary. The JavaScript engine intercepts the template before evaluation, splitting it cleanly into:

  1. An immutable array of trusted static HTML chunks authored by the developer.
  2. A separate array of dynamic runtime values provided by variables.

Because the tag function inspects every dynamic value individually, it can automatically escape dangerous characters (<, >, &, ", ') before merging them with the static HTML.


Technical Deep Dive & Specifications

Tagged Template Literal Function Signature

When you prefix a backtick template with a function name (e.g., html\

${title}

``), the JavaScript engine passes the raw arguments to that function:

function html(strings, ...values) {
  // strings: Array of static string pieces (length = values.length + 1)
  // strings.raw: Array of raw unescaped strings
  // values: Array of interpolated expressions (${...})
}
Syntax: html`<p class="${className}">${userName}</p>`

strings: ["<p class=\"", "\">", "</p>"]  (Length: 3, Frozen)
values:  [className, userName]            (Length: 2)

Context-Aware HTML Escaping Rules

To prevent DOM XSS vulnerabilities, all interpolated strings injected into text contexts or attribute values must have dangerous characters replaced with their corresponding HTML entity codes:

Character Entity Replacement Vulnerability Prevented
& &amp; Prevents entity confusion / injection
< &lt; Prevents tag injection (<script>, <iframe>)
> &gt; Prevents breaking out of tags
" &quot; Prevents attribute breakout in <input value="...">
' &#39; / &#x27; Prevents attribute breakout in single-quoted attributes

Handling Arrays, Numbers, and Bypassed Safe HTML

A production-grade html tag function must handle multiple data types:

  1. Primitives (Strings, Numbers, Booleans): Escaped safely.
  2. Arrays (Lists of items): Flattened and recursively joined.
  3. Null / Undefined: Rendered as empty strings ("").
  4. Explicit Safe HTML (rawHtml): A wrapper object (e.g., { __html: string }) allowing intentional, pre-sanitized markup to bypass escaping.
                                  Value Type Check
                                         |
         +-------------------------------+-------------------------------+
         |                               |                               |
    Is Array?                      Is SafeWrapper?                  Is Primitive?
         |                               |                               |
  .map(process).join('')         Return .__html raw             escapeHTML(String(v))

💻 Interactive Code Playground

Starter Code

Line-by-Line Code Breakdown

  • Line 26–33 (function escapeHTML(str)): Transforms high-risk ASCII characters into safe HTML entities using regex replacement.
  • Line 36–38 (function raw(...)): Provides an explicit "escape hatch" for developer-authored, trusted HTML tags (e.g. verified badges).
  • Line 41–66 (function html(strings, ...values)): The tagged template dispatcher. Iterates through the static string tokens and selectively sanitizes dynamic values.
  • Line 49 (Array.isArray(val)): Handles mapped arrays (such as ${users.map(renderUserCard)}) by concatenating their pre-evaluated HTML output.
  • Line 79–86 (users[1]): Contains malicious <script> and onerror attack vectors.
  • Line 90–99 (renderUserCard): Declarative JSX-like syntax without needing Babel, React, or build steps.
  • Line 104 (${users.map(renderUserCard)}): Demonstrates functional composability with list mapping.

Expected Browser Render Output

(Crucially: Zero JavaScript alerts or popups execute when rendering the malicious user).


SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
HTML STARSHIP CODE TERMINAL playground.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...
Safe Dynamic HTML with Tagged Templates
Notice how malicious XSS payloads are safely neutralized into harmless plain text.

+-------------------------------------+  +-------------------------------------+
| Guillermo Rauch                     |  | <script>alert('Pwned!')</script>... |
| Role: CEO & Founder                 |  | Role: Penetration Tester            |
| Building the next generation cloud..|  | <b onmouseover=alert(1)>Hover me... |
| [ ✓ Verified Account ]              |  | Unverified                          |
+-------------------------------------+  +-------------------------------------+

🏋️ Hands-On Exercise

🎯 The Challenge: Dynamic E-Commerce Product Grid

Instructions:

  1. Use the provided html tagged template engine.
  2. Build a function renderProductCard(product) that formats:
    • Product title (escaped).
    • Rating stars (e.g. ★ 4.8 / 5.0).
    • Price formatted as currency (e.g. $99.99).
    • An "In Stock" badge (green safe HTML) or "Out of Stock" warning (red safe HTML).
    • An untrusted customer review quote (must be escaped!).
  3. Render a list of 3 products into the #catalog container.

🏁 Starter Code Sandbox

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
STARTER CODE SANDBOX exercise.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...

⚠️ Common Pitfalls

  1. Accidentally Escaping Developer-Authored HTML Components: If you call html\
    ${renderChild()}
    `andrenderChild()returns a string withoutraw(), the inner HTML tags (
    ,

    ) will be turned into <div>text! Ensure sub-component template functions return compatible objects or useraw()`.

  2. Unquoted Attribute Injections: Writing html\`without quotes aroundvalue="${userVal}"allows an attacker providingfoo onclick=steal()` to inject arbitrary attributes. Always quote your HTML attributes.
  3. Using Untrusted URLs in href or src: Escaping < and > does not protect against javascript: pseudo-protocols! If a user submits javascript:alert(1) as a website link, escaping will leave href="javascript:alert(1)" intact. Always validate URL protocols (http:, https:).

💡 Pro Tips

  1. Template Caching with strings Identity: In ES6, the strings array reference passed to a tagged template literal is identical (cached) across multiple executions of the same code location (strings1 === strings2). Modern libraries like Lit use this reference identity to parse the HTML template once into an internal <template> and only update dynamic slot bindings on subsequent renders!
  2. Leverage IDE Syntax Highlighting Extensions: Extensions like VS Code's Comment Tagged Templates or lit-html provide full syntax highlighting, autocomplete, and emmet support inside html\...`` blocks without needing a compile step.

📌 Key Takeaways

  • Tagged template literals intercept string evaluation, separating static markup from dynamic variables.
  • The first parameter is a frozen array of static string chunks; subsequent parameters contain runtime interpolations.
  • Building an html tag function allows automatic entity escaping (<, >, &, ", ') to block DOM XSS.
  • Safe bypass wrappers (raw()) allow intentional developer-authored sub-templates.
  • The strings array reference is cached by JavaScript engines, enabling high-performance template caching.
  • --
⭐ LEARN: HTML 🌟 ⚔️ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

In the tagged template call myTag\Hello ${name}, you have ${count} messages`, what is the length of the first argument (strings`)?

Question 1 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 2 / 3

Why does standard HTML entity escaping fail to protect against malicious input in <a href="${userLink}">?

Question 2 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 3 / 3

How do libraries like Lit achieve near-native rendering performance with tagged template literals?

Question 3 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP