Public API
The public API consists of all exported functions, macros, and types that are intended for end users. These APIs are stable and follow semantic versioning - breaking changes will only occur in major version updates. The public API includes the core @render
macro, component definition utilities like @component
and @deftag
, security features like SafeString
, and all the HTML element macros available through HypertextTemplates.Elements
. These are the building blocks you'll use to create templates and components in your applications.
HypertextTemplates.HypertextTemplates
— ModuleHypertextTemplates
A hypertext templating DSL for Julia that allows writing HTML using native Julia syntax.
HypertextTemplates provides a macro-based DSL where all HTML elements are exposed as macros (e.g., @div
, @p
). It uses a special {}
syntax for attributes that mimics NamedTuples, allowing natural integration of Julia control flow directly in templates.
Key Features
- Zero-allocation rendering: Direct IO streaming without intermediate DOM
- Component system: Reusable components with props and slots
- Auto-escaping: Automatic HTML escaping for security (bypass with
SafeString
) - Streaming support: Efficient chunked rendering for large documents
- Markdown integration: Render Markdown files as components (requires CommonMark.jl)
Basic Usage
using HypertextTemplates, HypertextTemplates.Elements
# Simple rendering
@render @div {class = "greeting"} "Hello, World!"
# Components with slots
@component function card(; title)
@div {class = "card"} begin
@h2 $title
@div {class = "body"} @__slot__
end
end
Main Exports
@render
: Render templates to strings or IO@component
: Define reusable components@deftag
: Create macro shortcuts for componentsSafeString
: Mark content as pre-escaped HTMLStreamingRender
: Iterator for chunked rendering@<
: Dynamic component/element rendering
See the documentation for detailed examples and advanced features.
HypertextTemplates.SafeString
— TypeSafeString(str::String)
A string wrapper that bypasses automatic HTML escaping.
By default, all string content is HTML-escaped to prevent XSS attacks. SafeString
marks content as pre-escaped or trusted HTML that should be rendered as-is.
Only use SafeString
with content you trust completely. Never wrap user input directly with SafeString
without proper sanitization. This can lead to XSS vulnerabilities.
Arguments
str::String
: The HTML string to mark as safe
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> # Normal strings are escaped
@render @div "<b>Bold</b>"
"<div><b>Bold</b></div>"
julia> # SafeString content is not escaped
@render @div $(SafeString("<b>Bold</b>"))
"<div><b>Bold</b></div>"
julia> # Common use case: pre-rendered markdown
markdown_html = "<p>Already <em>escaped</em> content</p>";
julia> @render @article $(SafeString(markdown_html))
"<article><p>Already <em>escaped</em> content</p></article>"
Security best practices
# GOOD: Content from trusted sources
html = markdown_to_html(user_content) # Markdown processor escapes content
@render @div SafeString(html)
# GOOD: Your own HTML generation
safe_html = "<span class="highlight">Important</span>"
@render @div SafeString(safe_html)
# BAD: Never do this with user input!
user_input = get_user_input()
@render @div SafeString(user_input) # DANGER: XSS vulnerability!
See also: @render
, escape_html
, @esc_str
HypertextTemplates.StreamingRender
— TypeStreamingRender(func; kwargs...)
StreamingRender(; kwargs...) do io
# render content
end
Create an iterator for streaming template rendering.
StreamingRender
enables efficient rendering of large templates by yielding chunks of output as they become available. This is particularly useful for:
- HTTP streaming responses
- Large documents that would consume too much memory if rendered at once
- Progressive rendering where content appears as it's generated
The function uses intelligent micro-batching: large writes (≥64 bytes) are sent immediately for low latency, while smaller writes are batched for efficiency.
Arguments
func
: Function that takes anio
argument to pass to@render
Keywords
buffer_size::Int=32
: Number of chunks the channel can buffer before blockingchunk_size::Int=4096
: Legacy parameter (kept for compatibility, no longer used)immediate_threshold::Int=64
: Bytes above which writes bypass buffering for low latency
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> # Collect all chunks (for demonstration)
chunks = String[];
julia> for chunk in StreamingRender() do io
@render io @div begin
@h1 "Header"
@p "Content"
end
end
push!(chunks, String(chunk))
end
julia> join(chunks)
"<div><h1>Header</h1><p>Content</p></div>"
HTTP streaming example
using HTTP
using HypertextTemplates, HypertextTemplates.Elements
function handle_request(req)
HTTP.Response(200, ["Content-Type" => "text/html"]) do io
for chunk in StreamingRender() do render_io
@render render_io @html begin
@head @title "Streaming Page"
@body begin
@h1 "Live Data"
for i in 1:1000
@div "Item $i"
end
end
end
end
write(io, chunk)
end
end
end
Progressive rendering
# Stream data as it becomes available
for chunk in StreamingRender() do io
@render io @div begin
@h1 "Results"
for result in fetch_results_lazily()
@article begin
@h2 result.title
@p result.content
end
end
end
end
# Send chunk to client immediately
write(client_connection, chunk)
flush(client_connection)
end
See also: @render
, MicroBatchWriter
HypertextTemplates.@<
— Macro@<component_or_element
@<component_or_element children...
@<component_or_element {props...}
@<component_or_element {props...} children...
Dynamically render a component or element from a variable.
The @<
macro enables dynamic component selection, where the component or element to render is determined at runtime. This is useful for polymorphic rendering, component mappings, and conditional component selection.
Arguments
component_or_element
: A variable containing a component function or elementprops...
: Optional properties in{}
syntaxchildren...
: Optional child content
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> # Dynamic element selection
tag = Elements.div
@render @<tag {class = "dynamic"} "Content"
"<div class=\"dynamic\">Content</div>"
julia> # Change element at runtime
tag = span
@render @<tag {class = "dynamic"} "Content"
"<span class=\"dynamic\">Content</span>"
Dynamic component selection
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> # Define components
@component function info_box(; message)
@div {class = "info"} @p $message
end;
julia> @component function error_box(; message)
@div {class = "error"} @strong $message
end;
julia> # Select component based on condition
function render_message(type, message)
component = type == :error ? error_box : info_box
@render @<component {message}
end
render_message (generic function with 1 method)
julia> render_message(:info, "All good!")
"<div class=\"info\"><p>All good!</p></div>"
julia> render_message(:error, "Something went wrong!")
"<div class=\"error\"><strong>Something went wrong!</strong></div>"
With slots
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function wrapper(; variant = "default")
@div {class = "wrapper-$variant"} @__slot__
end;
julia> # Dynamic wrapper
w = wrapper
@render @<w {variant = "special"} begin
@h1 "Wrapped content"
end
"<div class=\"wrapper-special\"><h1>Wrapped content</h1></div>"
See also: @component
, @deftag
HypertextTemplates.@__once__
— Macro@__once__ begin
# content to render once
end
Render content only once per @render
call.
The @__once__
macro ensures that its content is rendered only once, even if the containing component is used multiple times within the same render tree. This is essential for including CSS, JavaScript, or other resources that should not be duplicated.
Common use cases
- CSS styles that should appear once
- JavaScript libraries and initialization code
- Meta tags or link elements in components
- Any content that would cause issues if duplicated
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function button_with_styles(; text, variant = "primary")
@__once__ begin
@style """
.btn { padding: 10px; border: none; cursor: pointer; }
.btn-primary { background: blue; color: white; }
.btn-danger { background: red; color: white; }
"""
end
@button {class = "btn btn-$variant"} $text
end;
julia> @deftag macro button_with_styles end
@button_with_styles (macro with 1 method)
JavaScript dependencies
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function chart(; data)
@__once__ begin
@script {src = "https://cdn.plot.ly/plotly-latest.min.js"}
end
@div {id = "chart-$(hash(data))"} begin
@script """
Plotly.newPlot('chart-$(hash(data))', $(data));
"""
end
end;
julia> @deftag macro chart end
@chart (macro with 1 method)
Scope behavior
Each @render
call maintains its own set of rendered once-blocks:
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function with_header()
@__once__ @h1 "Page Header"
@p "Content"
end;
julia> @deftag macro with_header end
@with_header (macro with 1 method)
julia> # First render includes the header
@render @with_header
"<h1>Page Header</h1><p>Content</p>"
julia> # Second render also includes it (different @render call)
@render @with_header
"<h1>Page Header</h1><p>Content</p>"
See also: @component
, @render
HypertextTemplates.@__slot__
— Macro@__slot__ [name]
Define a content slot within a component.
Slots enable content projection - allowing parent components to pass content into specific locations within child components. This is essential for creating flexible, composable components.
Arguments
name
: Optional slot name. If omitted, creates the default slot.
Default slot
The default slot (no name) receives all content passed to the component that isn't assigned to a named slot.
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function card(; title)
@div {class = "card"} begin
@h2 $title
@div {class = "content"} @__slot__
end
end;
julia> @deftag macro card end
@card (macro with 1 method)
julia> @render @card {title = "Welcome"} begin
@p "This goes into the default slot"
@p "So does this"
end
"<div class=\"card\"><h2>Welcome</h2><div class=\"content\"><p>This goes into the default slot</p><p>So does this</p></div></div>"
Named slots
Named slots receive only content explicitly assigned to them using name := content
syntax.
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function modal(; title = "")
@div {class = "modal"} begin
@header begin
@h2 $title
@__slot__ header_actions
end
@div {class = "body"} @__slot__
@footer @__slot__ footer
end
end;
julia> @deftag macro modal end
@modal (macro with 1 method)
julia> @render @modal {title = "Confirm"} begin
# Default slot content
@p "Are you sure?"
# Named slot content
header_actions := @button {class = "close"} "×"
footer := begin
@button "Cancel"
@button {class = "primary"} "OK"
end
end
"<div class=\"modal\"><header><h2>Confirm</h2><button class=\"close\">×</button></header><div class=\"body\"><p>Are you sure?</p></div><footer><button>Cancel</button><button class=\"primary\">OK</button></footer></div>"
See also: @component
HypertextTemplates.@cm_component
— Macro@cm_component component_name(; props...) = "file_name.md"
Create a component from a Markdown file.
This macro creates a component that renders Markdown content from a file. The Markdown is parsed using CommonMark.jl and can include interpolated values using $variable
syntax.
This feature requires CommonMark.jl to be installed as it's provided via Julia's package extension mechanism.
Arguments
component_name
: Name for the component functionprops...
: Optional keyword arguments that can be interpolated in the Markdownfile_name.md
: Path to the Markdown file (relative to the current file)
Features
- Interpolation: Use
$prop_name
in Markdown to insert prop values - Live reload: With Revise.jl, changes to the Markdown file auto-update
- Compile-time parsing: Without Revise.jl, Markdown is parsed at compile time for performance
Examples
Basic usage
# In article.md:
# # $title
#
# By $author on $date
#
# $content
# In your Julia code:
using HypertextTemplates, HypertextTemplates.Elements
using CommonMark
@cm_component article(; title, author, date, content) = "article.md"
@deftag macro article end
@render @article {
title = "Hello World",
author = "Jane Doe",
date = "2024-01-15",
content = "This is my first post!"
}
Directory structure
# components/header.md contains:
# # $site_name
#
# *$tagline*
# In components/components.jl:
@cm_component header(; site_name, tagline = "Welcome") = "header.md"
# Path is relative to the file containing @cm_component
With default values
@cm_component footer(; copyright_year = 2024, company = "Acme Corp") = "footer.md"
@deftag macro footer end
# Uses defaults
@render @footer
# Override defaults
@render @footer {copyright_year = 2025}
Complex content interpolation
# In template.md:
# # Product: $name
#
# Price: $$$price
#
# $description
@cm_component product_card(; name, price, description) = "template.md"
@deftag macro product_card end
@render @product_card {
name = "Widget",
price = 19.99,
description = "A **fantastic** widget with _many_ features!"
}
When using Revise.jl, you can edit the Markdown file and see changes immediately without restarting Julia or redefining the component.
See also: @component
, SafeString
HypertextTemplates.@component
— Macro@component function component_name(; properties...)
# template body
end
Define a reusable component function.
Components are functions that return template content and can accept properties as keyword arguments. They enable code reuse and composition in templates.
After defining a component, use @deftag
to create a convenient macro for using it like a regular HTML element.
Arguments
component_name
: The name of the component functionproperties...
: Keyword arguments that become the component's props
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function greeting(; name = "World")
@h1 "Hello, " $name "!"
end
greeting (generic function with 1 method)
julia> @deftag macro greeting end
@greeting (macro with 1 method)
julia> @render @greeting {name = "Julia"}
"<h1>Hello, Julia!</h1>"
julia> @render @greeting # Uses default value
"<h1>Hello, World!</h1>"
Components with slots
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function card(; title)
@div {class = "card"} begin
@h2 $title
@div {class = "body"} @__slot__
end
end
card (generic function with 1 method)
julia> @deftag macro card end
@card (macro with 1 method)
julia> @render @card {title = "Info"} begin
@p "Card content goes here"
end
"<div class=\"card\"><h2>Info</h2><div class=\"body\"><p>Card content goes here</p></div></div>"
Typed props for safety
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function price(; amount::Number, currency::String = "USD")
@span {class = "price"} $currency " " $(round(amount, digits=2))
end
price (generic function with 1 method)
julia> @deftag macro price end
@price (macro with 1 method)
julia> @render @price {amount = 19.999}
"<span class=\"price\">USD 20.0</span>"
HypertextTemplates.@context
— Macro@context {key = value, ...} body
@context {key, ...} body
Set context values that will be available to all child components.
The @context
macro allows passing data through the component tree without explicit prop drilling. It uses Julia's IOContext
mechanism to store key-value pairs that can be retrieved by child components using @get_context
.
Supports both explicit key = value
syntax and shorthand key
syntax (which expands to key = key
).
Arguments
- Key-value pairs or shorthand symbols in
{}
syntax specifying the context data body
: The content that will have access to the context
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function themed_button(; text = "Click me")
theme = @get_context(:theme, "light")
@button {class = "btn-$theme"} $text
end;
julia> @deftag macro themed_button end
@themed_button (macro with 1 method)
julia> @render @context {theme = "dark"} begin
@themed_button {text = "Dark Button"}
end
"<button class=\"btn-dark\">Dark Button</button>"
julia> # Shorthand syntax
theme = "blue"
@render @context {theme} begin # Same as {theme = theme}
@themed_button
end
"<button class=\"btn-blue\">Click me</button>"
julia> # Mixed syntax
user = "alice"
@render @context {user, theme = "dark"} begin
@div begin
@text "User: " @get_context(:user)
@themed_button
end
end
"<div>User: alice<button class=\"btn-dark\">Click me</button></div>"
See also: @get_context
, @component
HypertextTemplates.@deftag
— Macro@deftag macro name end
@deftag name
Create a macro shortcut for using a component or element.
After defining a component with @component
, use @deftag
to create a convenient macro that allows using the component like an HTML element.
The macro name end
syntax is preferred as it allows the LSP to correctly track the macro definition location.
Arguments
name
: Symbol name of the component/element to create a macro for
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> # Define a component
@component function alert(; type = "info", dismissible = false)
@div {class = "alert alert-$type"} begin
@__slot__
if dismissible
@button {class = "close"} "×"
end
end
end
alert (generic function with 1 method)
julia> # Create macro (preferred syntax for LSP support)
@deftag macro alert end
@alert (macro with 1 method)
julia> # Now use like an HTML element
@render @alert {type = "warning"} "Watch out!"
"<div class=\"alert alert-warning\">Watch out!</div>"
julia> # Alternative syntax (works but no LSP support)
@component function message(; text)
@p {class = "message"} $text
end
message (generic function with 1 method)
julia> @deftag message
@message (macro with 1 method)
julia> @render @message {text = "Hello"}
"<p class=\"message\">Hello</p>"
Custom elements
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> # Define a custom HTML element
@element "my-widget" my_widget
julia> @deftag macro my_widget end
@my_widget (macro with 1 method)
julia> @render @my_widget {id = "w1"} "Custom element"
"<my-widget id=\"w1\">Custom element</my-widget>"
See also: @component
, @element
, @<
HypertextTemplates.@element
— Macro@element html_tag_name [julia_name]
Define a custom HTML element.
Creates a new HTML element that can be used with HypertextTemplates' macro syntax. This is useful for custom elements, web components, or HTML elements not included in the default set.
Arguments
html_tag_name
: The HTML tag name as it will appear in output (e.g., "my-element")julia_name
: Optional Julia identifier name (defaults tohtml_tag_name
with invalid characters replaced)
Examples
julia> using HypertextTemplates
julia> # Define a custom element
@element "my-widget" my_widget
julia> # Use with @deftag for macro syntax
@deftag macro my_widget end
@my_widget (macro with 1 method)
julia> @render @my_widget {id = "w1", class = "custom"} "Widget content"
"<my-widget id=\"w1\" class=\"custom\">Widget content</my-widget>"
Web Components
julia> using HypertextTemplates
julia> # Define web component elements
@element "ion-button" ion_button
julia> @element "ion-icon" ion_icon
julia> @deftag macro ion_button end
@ion_button (macro with 1 method)
julia> @deftag macro ion_icon end
@ion_icon (macro with 1 method)
julia> @render @ion_button {color = "primary", expand = "block"} begin
@ion_icon {name = "save-outline", slot = "start"}
"Save"
end
"<ion-button color=\"primary\" expand=\"block\"><ion-icon name=\"save-outline\" slot=\"start\"></ion-icon>Save</ion-button>"
See also: @deftag
, @component
HypertextTemplates.@esc_str
— Macro@esc_str
Escape HTML at compile time and return a SafeString
.
This string macro performs HTML escaping during macro expansion rather than at runtime, providing better performance for static content that needs escaping.
The resulting SafeString
will not be escaped again during rendering.
See also: SafeString
, escape_html
HypertextTemplates.@get_context
— Macro@get_context(key)
@get_context(key, default)
Retrieve a value from the current context.
The @get_context
macro retrieves values that were set by parent @context
blocks. It accesses the IOContext
chain to find the requested key.
Arguments
key
: The context key to retrieve (as a symbol or string)default
: Optional default value if the key is not found
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @component function user_greeting()
user = @get_context(:user, "Guest")
@p "Hello, $(user)!"
end;
julia> @deftag macro user_greeting end
@user_greeting (macro with 1 method)
julia> @render @context {user = "Alice"} begin
@user_greeting
end
"<p>Hello, Alice!</p>"
julia> # Without context, uses default
@render @user_greeting
"<p>Hello, Guest!</p>"
julia> # Access multiple context values
@component function profile_card()
user = @get_context(:user, "Unknown")
theme = @get_context(:theme, "light")
role = @get_context(:role) # No default
@div {class = "profile-$theme"} begin
@h3 $user
!isnothing(role) && @span {class = "role"} $role
end
end;
See also: @context
, @component
HypertextTemplates.@render
— Macro@render [destination] dom
Render a template to the given destination.
If no destination is provided, renders to a String
and returns it. The destination can be any IO object (e.g., stdout
, IOBuffer
, file handle) or a type like String
or Vector{UInt8}
.
This macro is only needed for rendering the root of the DOM tree, not for the output of each individual component that is defined.
Arguments
destination
: Optional IO object or type to render to (default:String
)dom
: The template expression to render
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> @render @div "Hello, World!"
"<div>Hello, World!</div>"
julia> buffer = IOBuffer();
julia> @render buffer @span {class = "greeting"} "Hi!";
julia> String(take!(buffer))
"<span class=\"greeting\">Hi!</span>"
julia> @render Vector{UInt8} @p "Binary output"
20-element Vector{UInt8}:
0x3c
0x70
0x3e
0x42
0x69
0x6e
0x61
0x72
0x79
0x20
0x6f
0x75
0x74
0x70
0x75
0x74
0x3c
0x2f
0x70
0x3e
Rendering to files
open("output.html", "w") do file
@render file @html begin
@head @title "My Page"
@body @h1 "Hello!"
end
end
See also: StreamingRender
, @component
HypertextTemplates.@text
— Macro@text content...
Explicitly render content as text within templates.
The @text
macro explicitly marks content for text rendering with HTML escaping. It is rarely needed directly since the $
interpolation syntax provides the same functionality more concisely.
Text content is automatically HTML-escaped unless wrapped in SafeString
.
Arguments
content...
: One or more values to render as text
Examples
julia> using HypertextTemplates, HypertextTemplates.Elements
julia> # HTML is escaped by default
@render @div @text "<script>alert('XSS')</script>"
"<div><script>alert('XSS')</script></div>"
julia> # Mix with elements
@render @div begin
@h1 "Title"
@text "Some text content"
@p "In a paragraph"
end
"<div><h1>Title</h1>Some text content<p>In a paragraph</p></div>"
The $
interpolation syntax is preferred over @text
in most cases:
# Preferred
@div "Count: " $count
# Equivalent but verbose
@div "Count: " @text count
See also: SafeString
, escape_html
HypertextTemplates.Elements
— ModuleHypertextTemplates.Elements
Standard HTML elements for use with HypertextTemplates.
This module provides all standard HTML5 elements as both functions and macros. Each element can be used either as a macro (e.g., @div
) or accessed as a function (e.g., Elements.div
).
Usage
using HypertextTemplates, HypertextTemplates.Elements
# Use elements as macros
@render @div {class = "container"} begin
@h1 "Title"
@p "Content"
end
# Or access them as values for dynamic rendering
element = rand() > 0.5 ? Elements.div : Elements.span
@render @<element "Dynamic content"
Available Elements
All standard HTML5 elements are available, including:
- Document structure:
html
,head
,body
,div
,span
- Text content:
p
,h1
-h6
,blockquote
,pre
- Forms:
form
,input
,textarea
,select
,button
- Media:
img
,video
,audio
,canvas
- Tables:
table
,tr
,td
,th
- And many more...
Special Behavior
- The
@html
element automatically includes<!DOCTYPE html>
- Void elements (like
br
,img
,input
) are self-closing