Skip to content

Templates & Frontend

GoForge uses templ for type-safe server-side templates and HTMX 2 for dynamic UI interactions.

Template Structure

internal/web/templates/
├── layouts/
│   └── base.templ         # HTML shell with sidebar, nav, scripts
├── pages/
│   ├── dashboard.templ    # Dashboard with stats cards
│   ├── login.templ        # Login form
│   ├── register.templ     # Registration form
│   ├── projects/          # Project list and detail views
│   ├── deployments/       # Deployment views
│   ├── services/          # Service catalog and instances
│   ├── settings/          # User settings
│   └── errors/            # Error pages (404, 500)
└── components/
    ├── sidebar.templ      # Navigation sidebar
    ├── nav.templ          # Top navigation bar
    ├── form.templ         # Reusable form elements
    ├── modal.templ        # Modal dialogs
    ├── toast.templ        # Toast notifications
    └── ...

Working with templ

Generating Templates

# Generate all templates
make templ

# Watch for changes (in a separate terminal)
make templ-watch

The make dev command handles this automatically via air.

Template Syntax

templ templates are Go files with HTML-like syntax:

package pages

import "github.com/raythurman2386/goforge/internal/models"

templ ProjectList(projects []*models.Project) {
    <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
        for _, p := range projects {
            <div class="card">
                <h3>{ p.Name }</h3>
                <p>{ p.Description }</p>
            </div>
        }
    </div>
}

Type Safety

templ provides compile-time type checking:

  • Template parameters are strongly typed
  • Go expressions are validated at compile time
  • Component composition is checked for correct argument types

Generated Files

templ generates *_templ.go files next to each .templ file. These are:

  • Excluded from version control (.gitignore)
  • Excluded from linting (.golangci.yml)
  • Regenerated by make templ or make build

HTMX Integration

HTMX enables dynamic UI without writing JavaScript. Common patterns used in GoForge:

Partial Page Updates

<button hx-post="/projects/{id}/deploy"
        hx-target="#deployment-status"
        hx-swap="innerHTML">
    Deploy
</button>

Form Submissions

<form hx-post="/projects"
      hx-target="#project-list"
      hx-swap="afterbegin">
    <!-- form fields -->
</form>

Server-Sent Events

<div hx-ext="sse"
     sse-connect="/sse/deployments/{id}/logs"
     sse-swap="message">
    <!-- Logs appear here in real-time -->
</div>

CSRF Token

The base template configures HTMX to send the CSRF token automatically:

<body hx-headers='{"X-CSRF-Token": "..."}'>

TailwindCSS

GoForge uses TailwindCSS for styling.

Building CSS

# Build CSS once
make css

# Watch for changes
make css-watch

Configuration

The Tailwind configuration is in tailwind.config.js:

  • Scans .templ and generated _templ.go files for class names
  • Uses a green/emerald color palette
  • Includes custom font families

Input and Output

  • Input: static/css/input.css (Tailwind directives)
  • Output: static/css/output.css (generated, not committed)

Adding a New Page

  1. Create the templ template in internal/web/templates/pages/:
// internal/web/templates/pages/newpage.templ
package pages

templ NewPage(data SomeData) {
    @layouts.Base("Page Title") {
        <h1>New Page</h1>
        // page content
    }
}
  1. Create or update the handler in internal/web/handlers/:
func (h *Handler) NewPage(w http.ResponseWriter, r *http.Request) {
    data := h.service.GetData(r.Context())
    pages.NewPage(data).Render(r.Context(), w)
}
  1. Add the route in internal/web/router.go:
r.Get("/newpage", handler.NewPage)
  1. Regenerate templates:
make templ