What is alacris-go?
alacris-go renders alacris web components from Go and templ. It is three layers plus an optional desktop host, each usable on its own.
What Go actually renders
Section titled “What Go actually renders”A component’s shadow content is produced by its setup() function when the
element connects, in the browser. There is no server-side rendering of
component internals, and alacris does not support declarative shadow DOM.
So what a Go server renders is the element itself:
return alacris.E("info-card"). Prop("tone", "warn"). Slot("title", text("Ada Lovelace")). SlotAs("p", "body", text("Wrote the first algorithm."))<info-card tone="warn"> <div slot="title">Ada Lovelace</div> <p slot="body">Wrote the first algorithm.</p></info-card>Wrote the first algorithm.
Slot content is light DOM, so it is in the first paint.
Two consequences:
- Slot content is real HTML in the document. It is in the first paint and in a crawler’s view of the page. Put content that matters for SEO, or for readers with JavaScript disabled, here.
- Everything inside the shadow root appears when the module loads. Between
first paint and that moment the element is present but empty.
alacris.Pendinghides it until then.
Why every prop can cross as an attribute
Section titled “Why every prop can cross as an attribute”alacris coerces an attribute using the type of the prop’s default in
define(). Look at what that means for an object default:
const coerce = (v, d) => { if (v === null) return typeof d === 'boolean' ? false : d; const t = typeof d; if (t === 'number') return +v; if (t === 'boolean') return v !== 'false'; if (d !== null && t === 'object') { try { return JSON.parse(v); } catch { return d; } } return v;};JSON.parse is the object path. Objects and arrays round-trip through
attributes, so a fully-formed component needs no post-load property
assignment and no hydration payload:
type point struct { X int `json:"x"` Y int `json:"y"`}return alacris.E("x-demo"). Prop("tags", []string{"a", "b"}). Prop("origin", point{X: 1, Y: 2}). Prop("lookup", map[string]int{"b": 2, "a": 1}). Prop("at", time.Date(2026, 8, 8, 12, 0, 0, 0, time.UTC)). Prop("timeout", 1500*time.Millisecond)<x-demo tags="["a","b"]" origin="{"x":1,"y":2}" lookup="{"a":1,"b":2}" at="2026-08-08T12:00:00Z" timeout="1500"></x-demo>Objects and arrays cross as JSON, so no post-load assignment is needed.
The page is complete before any JavaScript has run.
The layers
Section titled “The layers”| Layer | What it does | What it costs |
|---|---|---|
alacris | Render elements, serve the runtime. | Nothing. Ordinary request/response. |
gen + CLI | Typed Go wrappers from your define() calls. | A generate step, next to templ’s. |
live | Server-driven props and component events. | A stateful server and session affinity. |
app | The same live handler, in an OS webview. | CGO, -tags desktop, a nested module. |
Most applications want the first two. Use the third when the server owns what the page shows (a shared board, a dashboard, a queue) and you would otherwise be writing polling or a custom WebSocket protocol. Use the fourth when that same process should be a window instead of a URL.
The module’s example is the third layer in a page, and the fourth with a flag:
examples/todo is a live Kanban board whose chrome is Alacris UI and whose
state lives on the server.
go run ./examples/todogo run -tags desktop ./examples/todo -desktopWhat this is not
Section titled “What this is not”- Not a component framework. Component internals are JavaScript, because
setup()runs in a browser. alacris-go does not invent a Go DSL for templates and signals. - Not a replacement for htmx or Datastar. Those swap HTML. This writes properties, which is a smaller and more precise operation, but only works for state a component already models as a prop.
- Not tied to the live layer. The first two layers are stateless and have no dependency on the third.
- Not Wails, not Fyne, not Tauri’s JS bindings. The desktop host reuses
live.OnandHandle.Set. Native dialogs and menus are Go.
Where to go next
Section titled “Where to go next”- Installation: serve the runtime and get a component on a page.
- Your first component: write one, generate its wrapper, use it.
- Alacris UI: sixty-eight Material Design 3
components, a theme engine, and
Config.UIto turn it on. - Live: props from the server: one property write, every open tab.
- Desktop apps: the same handler, in an OS webview.
- Props and encoding: the encoding table, and the two rules that exist because of sharp edges in the runtime.