Search documentation

Find a page in the Andurel docs.

Frontend

Andurel supports client-owned pages with Inertia and Vue, React, or Svelte as the default, or server-owned HTML with Templ and Datastar. Both use the same Echo router, models, services, sessions, validation, and PostgreSQL infrastructure. The choice is where rendering and interaction state live.

New projects default to Inertia React/pnpm. Pass --ui templ/datastar (or another Inertia combo) at andurel new time. The choice is stored in andurel.toml (project.inertia, project.javascriptPackageManager) and drives later generators.

Choose by ownership

Concern Inertia (default) Templ and Datastar
Primary renderer Vue, React, or Svelte browser runtime Go server
Initial response Templ root containing page JSON; optional SSR Complete Templ HTML
Navigation Inertia visits swap page components Browser requests and targeted Datastar actions
Interaction state Frontend component and application state Server state plus small signals
Data contract JSON payload structs and TypeScript types Typed Templ parameters
Partial work Partial reloads, lazy/deferred/once props, merge metadata Named fragments and SSE events
JavaScript surface Full frontend ecosystem Small by default
Strong fit App-like navigation and rich local interaction Forms, content, operations screens, server-led workflows

Complexity alone does not decide it. A small Inertia page still introduces a client runtime and JSON boundary, while Templ can stream live multi-fragment updates.

Working with Inertia

An Inertia controller names a frontend page and crosses an explicit JSON boundary:

1return c.renderer.Page(etx, "Products/Index", inertia.Props{
2    "products": toProductResources(products),
3}).Render()

The first visit renders application-owned views/root.templ, embeds the page object, and loads Vite. Later visits return page JSON and the official adapter swaps the component. Go retains authorization, validation, queries, and payload construction; Vue, React, or Svelte owns page rendering and local interaction.

Define page payload structs with stable JSON tags and matching TypeScript declarations. Do not serialize database models or narsilc rows directly. Partial reloads and prop policies reduce work but do not remove the need for a deliberate browser API. See Inertia and Props.

Working with Templ

A controller passes typed values directly into a compiled component:

1func (c Products) Index(etx *echo.Context) error {
2    products, err := c.products.List(etx.Request().Context())
3    if err != nil {
4        return err
5    }
6    return hypermedia.RenderPage(etx, views.ProductsIndex(products))
7}

There is no serialization layer between controller and view. Parameter or type changes fail compilation. RenderPage writes full HTML, RenderComponent is its readability alias, and RenderFragment(s) extracts named fragments.

Datastar adds targeted interaction without creating a separate SPA. A controller reads signals, validates and applies domain work, then emits an SSE patch:

1var signals ArchiveSignals
2if err := hypermedia.ReadSignals(etx.Request(), &signals); err != nil {
3    return err
4}
5if err := c.products.Archive(etx.Request().Context(), signals.ID); err != nil {
6    return err
7}
8return hypermedia.PatchComponent(etx, views.ProductRow(signals.ID))

Patches can replace, remove, append, prepend, insert, or update named fragments. Signals, custom events, URL changes, scripts, and long-lived broadcasters form the server-to-browser protocol. The application owns fragment identifiers and state transitions. See Templ & Datastar.

Forms and validation

Inertia normally uses redirect-after-write. Pass mapped errors with Page(...).ValidationErrors(...), respect named error bags, preserve flash through redirects, and let the client adapter expose errors to the form. See Shared Data and Redirects.

Templ commonly re-renders the form or a named fragment with typed submitted values and field errors, using a suitable status such as 422.

Domain validation should be shared. Only its transport and presentation differ.

Mixing both approaches

One Echo application can have Templ and Inertia routes. Keep each route's response contract consistent: an Inertia navigation must receive an Inertia page or explicit location response, not arbitrary HTML.

Resource generation follows the UI recorded in andurel.toml. In an Inertia project, scaffolds and controllers emit Inertia pages for that adapter. Use --api for JSON handlers instead of pages:

1andurel generate scaffold Product
2andurel generate scaffold Product --api
3andurel sync routes

sync routes creates typed TypeScript URL helpers from marked route declarations. Templ and frontend layouts are not shared components: one renders in Go, the other in the client runtime.

A clean mixed boundary is public Templ marketing pages beside an authenticated Inertia application. Avoid implementing the same feature twice without a concrete reason. To add Templ resources inside an Inertia app, change the project UI in andurel.toml or hand-author Templ handlers; generators follow the manifest.

Development, assets, and SSR

Both choices compile Templ because base documents and email use it:

1andurel sync views

Inertia additionally runs Vite during development and embeds production output in the Go binary. The package manager recorded in andurel.toml controls dependency installation; it does not select the Node runtime used by cmd/ssr.

SSR is per page: call .SSR() on the page builder. In development the renderer posts to Vite; otherwise cmd/app posts to INERTIA_SSR_URL and cmd/ssr owns Node. Client rendering is the fallback unless fail-fast is enabled. See SSR.

Migration cost

Inertia to Templ moves render state into Go and replaces local interactions with browser requests or Datastar signals and patches. Templ to Inertia replaces typed component calls with a JSON contract, recreates UI in a frontend framework, and adopts client navigation and form semantics.

Models and services should survive either move. If they must change, frontend responsibilities have leaked into the domain boundary.