Core Concepts

Understanding the core concepts of HypertextTemplates.jl will help you use the library effectively and write maintainable templates.

Macro-Based DSL Philosophy

HypertextTemplates.jl uses Julia's macro system to create a domain-specific language (DSL) for HTML generation. This approach provides several benefits:

Compile-Time Optimization

using HypertextTemplates
using HypertextTemplates.Elements

# This macro call...
html = @render @div {class = "container"} @p "Hello"

<div class="container"><p>Hello</p></div>

Native Julia Integration

The DSL feels like natural Julia code because it is Julia code:

using HypertextTemplates
using HypertextTemplates.Elements

# Regular Julia control flow works seamlessly
@render @ul begin
    for i in 1:5
        if isodd(i)
            @li {class = "odd"} "Item " $i
        else
            @li {class = "even"} "Item " $i
        end
    end
end

<ul>
  <li class="odd">Item 1</li>
  <li class="even">Item 2</li>
  <li class="odd">Item 3</li>
  <li class="even">Item 4</li>
  <li class="odd">Item 5</li>
</ul>

Type Safety

Julia's type system helps catch errors at compile time:

using HypertextTemplates
using HypertextTemplates.Elements

@component function typed_list(; items::Vector{String})
    @ul begin
        for item in items
            @li $item
        end
    end
end

@deftag macro typed_list end

# Julia's type system helps catch errors
items = ["Apple", "Banana", "Cherry"]
html = @render @typed_list {items}

<ul>
  <li>Apple</li>
  <li>Banana</li>
  <li>Cherry</li>
</ul>

The {} Attribute Syntax

Building on the macro foundation, attributes use a special {} syntax that resembles Julia's NamedTuple syntax:

Basic Attributes

using HypertextTemplates
using HypertextTemplates.Elements

# Simple attributes
@render @div {id = "main", class = "container"} "Content"

<div id="main" class="container">Content</div>

using HypertextTemplates
using HypertextTemplates.Elements

# Computed attributes
width = 100
@render @img {src = "/logo.png", width = width * 2}

<img src="/logo.png" width="200">

Attribute Name Shortcuts

When variable names match attribute names:

using HypertextTemplates
using HypertextTemplates.Elements

class = "active"
disabled = true

# Instead of {class = class, disabled = disabled}
@render @button {class, disabled} "Click me"

<button class="active" disabled>Click me</button>

Attribute Spreading

Spread multiple attributes from a collection:

using HypertextTemplates
using HypertextTemplates.Elements

common_attrs = (class = "btn", type = "button")
@render @button {id = "submit", common_attrs...} "Submit"

<button id="submit" class="btn" type="button">Submit</button>

Boolean Attributes

Boolean handling follows HTML5 semantics:

using HypertextTemplates
using HypertextTemplates.Elements

# true renders the attribute name only
@render @input {type = "checkbox", checked = true}

<input type="checkbox" checked>

# false omits the attribute entirely
@render @input {type = "checkbox", checked = false}

<input type="checkbox">

Text Rendering and Interpolation

Variable Interpolation with $

The $ syntax marks expressions for rendering with automatic escaping:

using HypertextTemplates
using HypertextTemplates.Elements

user_input = "<script>alert('xss')</script>"
html = @render @p "User said: " $user_input

<p>User said: &lt;script&gt;alert('xss')&lt;/script&gt;</p>

The @text Macro

The $ syntax is actually shorthand for @text:

using HypertextTemplates
using HypertextTemplates.Elements

value = 42

# These are equivalent
html1 = @render @p "\$ Value: " $value

<p>$ Value: 42</p>

html2 = @render @p "@text Value: " @text value

<p>@text Value: 42</p>

a, b = 10, 20
# @text can handle complex expressions
html3 = @render @p @text "The sum is $(a + b)"

<p>The sum is 30</p>

Mixed Content

You can mix different content types:

using HypertextTemplates
using HypertextTemplates.Elements

dynamic_var = "dynamic content"

html = @render @div begin
    @span "Static text "   # String literal
    @code $dynamic_var     # Escaped variable
    @p "bold"              # Nested element
    @strong " more text"   # Another literal
end

<div>
  <span>Static text </span>
  <code>dynamic content</code>
  <p>bold</p>
  <strong> more text</strong>
</div>

Zero-Allocation Design

The performance benefits of the macro system extend to the rendering pipeline through zero-allocation design:

Direct IO Streaming

Instead of building a DOM tree, content streams directly to IO:

using HypertextTemplates
using HypertextTemplates.Elements

# No intermediate string allocations
io = IOBuffer()
@render io @div begin
    for i in 1:5
        @p "Paragraph " $i
    end
end

result = String(take!(io))

<div>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
  <p>Paragraph 3</p>
  <p>Paragraph 4</p>
  <p>Paragraph 5</p>
</div>

Efficient String Building

The rendering process uses Julia's efficient IO system:

using HypertextTemplates
using HypertextTemplates.Elements

# Internally uses write() calls, not string concatenation
html = @render @div begin
    @h1 "Title"
    @p "Content"
end


# This is equivalent to direct write() calls:
# write(io, "<div>")
# write(io, "<h1>")
# write(io, "Title")
# write(io, "</h1>")
# ...

<div>
  <h1>Title</h1>
  <p>Content</p>
</div>

Rendering Pipeline

The rendering pipeline works as follows:

  1. Macro Expansion: Templates are transformed into Julia code at compile time
  2. IO Target: All output goes to an IO stream (provided or created)
  3. Direct Writing: HTML strings and escaped content are written directly
  4. No Buffering: Content flows straight through without intermediate storage

This design means:

  • Memory usage is constant regardless of output size
  • First byte is written immediately (no buffering)
  • Suitable for very large documents
  • Optimal for streaming responses

Control Flow Integration

Since templates are Julia code, all control flow constructs work naturally:

Loops

using HypertextTemplates
using HypertextTemplates.Elements

# for loops
collection = ["Apple", "Banana", "Cherry"]
html1 = @render @ul for item in collection
    @li $item
end

<ul>
  <li>Apple</li>
  <li>Banana</li>
  <li>Cherry</li>
</ul>

# while loops
count = 0
html2 = @render @div begin
    while count < 3
        @p "Count: " $count
        global count += 1
    end
end

<div>
  <p>Count: 0</p>
  <p>Count: 1</p>
  <p>Count: 2</p>
</div>

# comprehensions
html3 = @render @select begin
    [@option {value = i} "Option " $i for i in 1:5]
end
println("\nComprehension:")

<select>
  <option value="1">Option 1</option>
  <option value="2">Option 2</option>
  <option value="3">Option 3</option>
  <option value="4">Option 4</option>
  <option value="5">Option 5</option>
</select>

Conditionals

All conditional forms are supported:

using HypertextTemplates
using HypertextTemplates.Elements

# if-else
condition = true
html1 = @render @div begin
    if condition
        @p "True branch"
    else
        @p "False branch"
    end
end

<div><p>True branch</p></div>

# ternary operator
isactive = false
html2 = @render @p {class = isactive ? "active" : "inactive"} "Status"

<p class="inactive">Status</p>

# short-circuit evaluation
hasdata = false
html3 = @render @div begin
    hasdata && @p "Data is available"
    !hasdata && @p "No data available"
end

<div><p>No data available</p></div>

Pattern Matching

Works with any macro-based control flow:

# With Match.jl (example)
@div begin
    @match value begin
        1 => @p "One"
        2 => @p "Two"
        _ => @p "Other"
    end
end

Component Architecture

The macro system and control flow integration come together in HypertextTemplates' component architecture:

Function-Based Components

using HypertextTemplates
using HypertextTemplates.Elements

@component function alert(; type = "info", message)
    classes = "alert alert-" * type
    @div {class = classes, role = "alert"} $message
end

@deftag macro alert end

# Use the component
html = @render @alert {type = "warning", message = "This is a warning!"}

<div class="alert alert-warning" role="alert">This is a warning!</div>

Composition

Components compose naturally:

using HypertextTemplates
using HypertextTemplates.Elements

# Reuse the alert component from above
@component function alert(; type = "info", message)
    classes = "alert alert-" * type
    @div {class = classes, role = "alert"} $message
end

@deftag macro alert end

@component function alert_list(; alerts)
    @div {class = "alert-container"} begin
        for alert in alerts
            @alert {type = alert.type, message = alert.message}
        end
    end
end

@deftag macro alert_list end

# Use the composed component
alerts = [
    (type = "info", message = "Information message"),
    (type = "warning", message = "Warning message"),
    (type = "error", message = "Error message")
]

html = @render @alert_list {alerts}

<div class="alert-container">
  <div class="alert alert-info" role="alert">Information message</div>
  <div class="alert alert-warning" role="alert">Warning message</div>
  <div class="alert alert-error" role="alert">Error message</div>
</div>

Component Transformation

The @component macro transforms the function to:

  1. Accept rendering context (IO stream)
  2. Handle slot content (both default and named slots)
  3. Manage source location tracking (in development mode)
  4. Support streaming render (for async operations)

For example, this component:

@component function example(; prop)
    @div $prop
end

Is transformed into a function that:

  • Accepts an internal __io__ parameter for rendering
  • Can receive slot content via hidden parameters
  • Tracks its definition location for debugging
  • Works seamlessly with StreamingRender

Performance Considerations

The zero-allocation design and macro system combine to optimize performance at multiple levels:

Compile-Time Work

using HypertextTemplates
using HypertextTemplates.Elements

# This template structure is analyzed at compile time
@component function static_heavy()
    @div {class = "wrapper"} begin
        @header begin
            @nav begin
                @ul begin
                    @li @a {href = "/"} "Home"
                    @li @a {href = "/about"} "About"
                end
            end
        end
    end
end

@deftag macro static_heavy end

# The structure is compiled, not interpreted at runtime
html = @render @static_heavy

<div class="wrapper">
  <header>
    <nav>
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  </header>
</div>

Runtime Efficiency

Only dynamic parts are computed at runtime:

using HypertextTemplates
using HypertextTemplates.Elements

@component function dynamic_list(; items)
    # Static structure compiled, only loop runs at runtime
    @ul {class = "list"} begin
        for item in items  # Only this loop runs at runtime
            @li $item
        end
    end
end

@deftag macro dynamic_list end

# Only the loop execution is runtime work
items = ["Dynamic 1", "Dynamic 2", "Dynamic 3"]
html = @render @dynamic_list {items}

<ul class="list">
  <li>Dynamic 1</li>
  <li>Dynamic 2</li>
  <li>Dynamic 3</li>
</ul>

HTML Escaping Strategy

Complementing the performance features, HypertextTemplates provides automatic security through its escaping strategy:

Automatic Escaping

All dynamic content is escaped by default:

using HypertextTemplates
using HypertextTemplates.Elements

unsafe = "<script>alert('xss')</script>"
html = @render @p $unsafe

<p>&lt;script&gt;alert('xss')&lt;/script&gt;</p>

Escape Rules

  1. String literals in templates are NOT escaped (trusted content)
  2. Variables and expressions with $ or @text ARE escaped
  3. Attribute values from variables ARE escaped
  4. Element content from components is already rendered (not double-escaped)

Performance

Escaping uses optimized routines:

  • Fast-path for strings without special characters
  • Efficient replacement for strings with special characters
  • Minimal allocations during escaping

Summary

These core concepts build on each other:

  1. Macros provide the foundation with compile-time optimization
  2. {} attributes extend the macro syntax for properties
  3. Text rendering handles content with automatic security
  4. Zero-allocation design ensures performance at scale
  5. Control flow integration leverages Julia's expressiveness
  6. Components combine all concepts for reusable templates

Each concept reinforces the others, creating a cohesive system that's fast, safe, and natural to use.