<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Template Engine</title>
<meta name="description" content="A programming language that compiles to HTML, CSS, and JavaScript. Build SPAs and static sites with built-in components, reactivity, routing, i18n, and animations.">
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<div id="app">
<div class="wf-row">
<div class="wf-container wf-animate-fadeIn">
<div class="wf-spacer"></div>
<h1 class="wf-heading wf-heading--h1">Template Engine</h1>
<p class="wf-text wf-text--muted">Use WebFluent as a server-side template engine from Rust and Node.js.</p>
<div class="wf-spacer"></div>
<div class="wf-alert wf-alert--info">WebFluent can be used as a server-side template engine from Rust and Node.js to render .wf templates into HTML or PDF with JSON data.</div>
<div class="wf-spacer"></div>
<h2 class="wf-heading wf-heading--h2">CLI Usage</h2>
<p class="wf-text">Render any .wf template with JSON data directly from the command line.</p>
<div class="wf-spacer"></div>
<div class="wf-card wf-card--outlined">
<div class="wf-card__body">
<code class="wf-code"># Render to HTML
wf render template.wf --data data.json --format html -o output.html
# Render to HTML fragment (no <html> wrapper)
wf render template.wf --data data.json --format fragment
# Render to PDF
wf render template.wf --data data.json --format pdf -o report.pdf
# Pipe JSON from stdin
echo '{"name":"Monzer"}' | wf render template.wf --format html
# With theme
wf render template.wf --data data.json --format html --theme dark</code>
</div>
</div>
<div class="wf-spacer"></div>
<table class="wf-table">
<thead>
<td>Option</td>
<td>Description</td>
</thead>
<tr>
<td>template</td>
<td>Path to the .wf template file</td>
</tr>
<tr>
<td>--data</td>
<td>Path to JSON data file (reads stdin if omitted)</td>
</tr>
<tr>
<td>--format, -f</td>
<td>Output format: html, fragment, or pdf</td>
</tr>
<tr>
<td>--output, -o</td>
<td>Output file path (stdout if omitted)</td>
</tr>
<tr>
<td>--theme</td>
<td>Theme name (default: "default")</td>
</tr>
</table>
<div class="wf-spacer"></div>
<hr class="wf-divider">
<div class="wf-spacer"></div>
<h2 class="wf-heading wf-heading--h2">Template Syntax</h2>
<p class="wf-text">Templates use standard .wf syntax. Data is passed as a JSON object — top-level keys become template variables.</p>
<div class="wf-spacer"></div>
<div class="wf-grid" style="grid-template-columns: repeat(2, 1fr)">
<div class="wf-card wf-card--outlined">
<div class="wf-card__header">
<span class="wf-badge wf-badge--primary">Template</span>
<p class="wf-text wf-text--bold">invoice.wf</p>
</div>
<div class="wf-card__body">
<code class="wf-code">Page Invoice (path: "/", title: "Invoice") {
Container {
Heading("Invoice #{number}", h1)
Text("Customer: {customer.name}")
Table {
Thead { Trow { Tcell("Item") Tcell("Price") } }
for item in items {
Trow {
Tcell(item.name)
Tcell("${item.price}")
}
}
}
if paid {
Badge("PAID", success)
} else {
Badge("UNPAID", danger)
}
}
}</code>
</div>
</div>
<div class="wf-card wf-card--outlined">
<div class="wf-card__header">
<span class="wf-badge wf-badge--info">Data</span>
<p class="wf-text wf-text--bold">data.json</p>
</div>
<div class="wf-card__body">
<code class="wf-code">{
"number": "INV-001",
"customer": { "name": "Acme Corp" },
"items": [
{ "name": "Widget", "price": 9.99 },
{ "name": "Gadget", "price": 24.99 }
],
"paid": true
}</code>
</div>
</div>
</div>
<div class="wf-spacer"></div>
<hr class="wf-divider">
<div class="wf-spacer"></div>
<h2 class="wf-heading wf-heading--h2">Rust API</h2>
<p class="wf-text">Add WebFluent as a library dependency to use templates in your Rust application.</p>
<div class="wf-spacer"></div>
<div class="wf-card wf-card--outlined">
<div class="wf-card__header">
<p class="wf-text wf-text--bold wf-text--muted">Cargo.toml</p>
</div>
<div class="wf-card__body">
<code class="wf-code">[dependencies]
webfluent = "0.2"</code>
</div>
</div>
<div class="wf-spacer"></div>
<div class="wf-card wf-card--outlined">
<div class="wf-card__header">
<p class="wf-text wf-text--bold wf-text--muted">main.rs</p>
</div>
<div class="wf-card__body">
<code class="wf-code">use webfluent::Template;
use serde_json::json;
fn main() -> webfluent::Result<()> {
let tpl = Template::from_file("templates/invoice.wf")?;
// HTML document (with embedded CSS)
let html = tpl.render_html(&json!({
"number": "INV-001",
"customer": { "name": "Acme Corp" },
"items": [{ "name": "Widget", "price": 9.99 }],
"paid": true
}))?;
// HTML fragment (no wrapper)
let fragment = tpl.render_html_fragment(&data)?;
// PDF bytes
let pdf_bytes = tpl.render_pdf(&data)?;
std::fs::write("invoice.pdf", pdf_bytes)?;
// With custom theme
let dark = Template::from_file("invoice.wf")?
.with_theme("dark")
.with_tokens(&[("color-primary", "#8B5CF6")]);
let html = dark.render_html(&data)?;
Ok(())
}</code>
</div>
</div>
<div class="wf-spacer"></div>
<hr class="wf-divider">
<div class="wf-spacer"></div>
<h2 class="wf-heading wf-heading--h2">Node.js API</h2>
<p class="wf-text">Use WebFluent templates in Express, Next.js, or any Node.js application.</p>
<div class="wf-spacer"></div>
<div class="wf-card wf-card--outlined">
<div class="wf-card__header">
<p class="wf-text wf-text--bold wf-text--muted">Install</p>
</div>
<div class="wf-card__body">
<code class="wf-code">npm install @aspect/webfluent</code>
</div>
</div>
<div class="wf-spacer"></div>
<div class="wf-card wf-card--outlined">
<div class="wf-card__header">
<p class="wf-text wf-text--bold wf-text--muted">Basic Usage</p>
</div>
<div class="wf-card__body">
<code class="wf-code">const { Template } = require('@aspect/webfluent');
const tpl = Template.fromFile('templates/invoice.wf');
// or: Template.fromString('Container { Heading("Hello!", h1) }');
// Render to HTML
const html = tpl.renderHtml({ name: "World" });
// Render to HTML fragment
const frag = tpl.renderHtmlFragment({ name: "World" });
// Render to PDF (returns Buffer)
const pdf = tpl.renderPdf({ name: "World" });
fs.writeFileSync('output.pdf', pdf);</code>
</div>
</div>
<div class="wf-spacer"></div>
<div class="wf-card wf-card--outlined">
<div class="wf-card__header">
<p class="wf-text wf-text--bold wf-text--muted">Express.js Example</p>
</div>
<div class="wf-card__body">
<code class="wf-code">const express = require('express');
const { Template } = require('@aspect/webfluent');
const app = express();
app.get('/invoice/:id', async (req, res) => {
const invoice = await db.getInvoice(req.params.id);
const tpl = Template.fromFile('templates/invoice.wf');
res.send(tpl.renderHtml(invoice));
});
app.get('/invoice/:id/pdf', async (req, res) => {
const invoice = await db.getInvoice(req.params.id);
const tpl = Template.fromFile('templates/invoice.wf');
res.type('application/pdf').send(tpl.renderPdf(invoice));
});</code>
</div>
</div>
<div class="wf-spacer"></div>
<hr class="wf-divider">
<div class="wf-spacer"></div>
<h2 class="wf-heading wf-heading--h2">Supported Features</h2>
<p class="wf-text">Templates support the static, data-driven subset of WebFluent.</p>
<div class="wf-spacer"></div>
<div class="wf-grid" style="grid-template-columns: repeat(2, 1fr)">
<div class="wf-card wf-card--elevated">
<div class="wf-card__header">
<h3 class="wf-heading wf-heading--h3">Supported</h3>
</div>
<div class="wf-card__body">
<div class="wf-stack">
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">All layout components</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">Typography (Text, Heading, Code)</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">Data display (Card, Table, List, Badge)</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">for loops over data arrays</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">if/else conditionals</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">String interpolation {var}</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">Nested access (user.name)</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">Design tokens and themes</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">Style blocks</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--success">Yes</span>
<p class="wf-text">PDF components</p>
</div>
</div>
</div>
</div>
<div class="wf-card wf-card--elevated">
<div class="wf-card__header">
<h3 class="wf-heading wf-heading--h3">Not Supported</h3>
</div>
<div class="wf-card__body">
<div class="wf-stack">
<div class="wf-row">
<span class="wf-badge wf-badge--danger">No</span>
<p class="wf-text">state / derived / effect</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--danger">No</span>
<p class="wf-text">Events (on:click, on:submit)</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--danger">No</span>
<p class="wf-text">Navigation / Router</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--danger">No</span>
<p class="wf-text">Stores (shared state)</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--danger">No</span>
<p class="wf-text">fetch (data loading)</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--danger">No</span>
<p class="wf-text">Animations</p>
</div>
<div class="wf-row">
<span class="wf-badge wf-badge--danger">No</span>
<p class="wf-text">Toast (imperative)</p>
</div>
</div>
</div>
</div>
</div>
<div class="wf-spacer"></div>
<hr class="wf-divider">
<div class="wf-spacer"></div>
<h2 class="wf-heading wf-heading--h2">Use Cases</h2>
<div class="wf-spacer"></div>
<div class="wf-grid" style="grid-template-columns: repeat(3, 1fr)">
<div class="wf-card wf-card--elevated">
<div class="wf-card__body">
<h3 class="wf-heading wf-heading--h3">Server-Rendered Pages</h3>
<p class="wf-text wf-text--muted">Generate HTML pages on the server with data from your database or API.</p>
</div>
</div>
<div class="wf-card wf-card--elevated">
<div class="wf-card__body">
<h3 class="wf-heading wf-heading--h3">PDF Reports</h3>
<p class="wf-text wf-text--muted">Create invoices, receipts, and reports as PDF files from structured data.</p>
</div>
</div>
<div class="wf-card wf-card--elevated">
<div class="wf-card__body">
<h3 class="wf-heading wf-heading--h3">Email Templates</h3>
<p class="wf-text wf-text--muted">Render HTML emails with WebFluent components and your design system.</p>
</div>
</div>
</div>
<div class="wf-spacer"></div>
</div>
</div>
</div>
<script src="../app.js"></script>
</body>
</html>