Fuego uses templ for type-safe HTML templates. Templ provides compile-time validation and excellent Go integration.
Why templ?
- Type-safe - Compile-time checking catches errors early
- Fast - No runtime template parsing
- Go-native - Full Go code completion in templates
- Small - Smaller binary size than text/template
File Conventions
| File | Purpose | Route |
|---|
page.templ | Page content | URL path |
layout.templ | Wraps pages | Inherited by children |
Creating Pages
Basic Page
Create app/dashboard/page.templ:
package dashboard
templ Page() {
<div class="container">
<h1>Dashboard</h1>
<p>Welcome to your dashboard!</p>
</div>
}
This maps to /dashboard.
Page with Props
package dashboard
type PageProps struct {
User User
Stats DashboardStats
}
templ Page(props PageProps) {
<div class="container">
<h1>Welcome, { props.User.Name }!</h1>
<div class="stats">
<p>Total tasks: { fmt.Sprint(props.Stats.TaskCount) }</p>
</div>
</div>
}
Creating Layouts
Root Layout
Create app/layout.templ:
package app
templ Layout(title string) {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>{ title } - My App</title>
<link rel="stylesheet" href="/docs/static/css/output.css"/>
<script src="https://unpkg.com/[email protected]"></script>
</head>
<body class="bg-gray-100 min-h-screen">
<nav class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4">
<a href="/docs/" class="text-xl font-bold">My App</a>
</div>
</nav>
<main class="max-w-7xl mx-auto py-6">
{ children... }
</main>
</body>
</html>
}
Layouts must include { children... } where page content should appear.
Nested Layouts
Create app/dashboard/layout.templ:
package dashboard
templ Layout() {
<div class="flex">
<aside class="w-64 bg-gray-800 text-white">
<nav>
<a href="/docs/dashboard">Overview</a>
<a href="/docs/dashboard/settings">Settings</a>
</nav>
</aside>
<div class="flex-1">
{ children... }
</div>
</div>
}
Nested layouts override parent layouts.
Dynamic Pages
URL Parameters
Create app/users/[id]/page.templ:
package users
templ Page(user User) {
<div class="user-profile">
<h1>{ user.Name }</h1>
<p>{ user.Email }</p>
</div>
}
Access via /users/123.
Components
Creating Components
package components
templ Button(text string, variant string) {
<button class={ "btn", "btn-" + variant }>
{ text }
</button>
}
templ Card(title string) {
<div class="card">
<h2>{ title }</h2>
<div class="card-body">
{ children... }
</div>
</div>
}
Using Components
package dashboard
import "myapp/app/_components"
templ Page() {
<div>
@components.Card("Statistics") {
<p>Your stats here</p>
}
@components.Button("Save", "primary")
</div>
}
HTMX Integration
Loading Data
templ TaskList() {
<div id="task-list" hx-get="/api/tasks" hx-trigger="load">
<p>Loading tasks...</p>
</div>
}
templ AddTaskForm() {
<form
hx-post="/api/tasks"
hx-target="#task-list"
hx-swap="innerHTML"
hx-on::after-request="this.reset()"
>
<input type="text" name="title" placeholder="New task..." required/>
<button type="submit">Add Task</button>
</form>
}
Toggle Actions
templ TaskItem(task Task) {
<li class="flex items-center gap-3">
<input
type="checkbox"
hx-post={ fmt.Sprintf("/api/tasks/toggle?id=%d", task.ID) }
hx-target="#task-list"
if task.Completed {
checked
}
/>
<span class={ templ.KV("line-through", task.Completed) }>
{ task.Title }
</span>
<button
hx-delete={ fmt.Sprintf("/api/tasks?id=%d", task.ID) }
hx-target="#task-list"
>
Delete
</button>
</li>
}
Full Example: Task Dashboard
// app/dashboard/page.templ
package dashboard
templ Page() {
<div class="px-4 py-5 sm:p-6">
<h1 class="text-2xl font-bold text-gray-900 mb-6">Dashboard</h1>
<div class="bg-white shadow rounded-lg p-6 mb-6">
<h2 class="text-lg font-medium text-gray-900 mb-4">Tasks</h2>
<!-- HTMX-powered task list -->
<div id="task-list" hx-get="/api/tasks" hx-trigger="load" hx-swap="innerHTML">
<div class="text-gray-500">Loading tasks...</div>
</div>
<!-- Add task form with HTMX -->
<form
class="mt-6 flex gap-4"
hx-post="/api/tasks"
hx-target="#task-list"
hx-swap="innerHTML"
hx-on::after-request="this.reset()"
>
<input
type="text"
name="title"
placeholder="New task..."
class="flex-1 rounded-md border-gray-300"
required
/>
<button
type="submit"
class="px-4 py-2 bg-orange-600 text-white rounded-md"
>
Add Task
</button>
</form>
</div>
</div>
}
Generating Pages
Use the CLI to generate pages:
# Basic page
fuego generate page dashboard
# Page with layout
fuego generate page admin/settings --with-layout
# Nested page
fuego generate page users/profile
Best Practices
Use components for reusability
Extract common UI patterns into components in a _components folder.
Pages should primarily compose components, not contain complex logic.
Use HTMX for interactivity
Let the server render HTML instead of writing client-side JavaScript.
templ catches errors during compilation. Fix warnings before deploying.
Next Steps