Building a larger feature
Individual examples show one subsystem each. Real pages compose several — server-rendered data, an interactive island, animation, maybe a live feed — on a single route. The constraint that shapes everything is the island memory model, so start there.
See it live: showcase (viz + animation on one page) and dashboard (nested routing).
The rule that drives the architecture
Island chunks share the main client's linear memory and run on a 4 KB stack, and a page may host one stateful chunk — any chunk that keeps module-level globals. Break the rule and two islands stomp each other's state. So the design move for every feature page is the same:
Render as much as possible server-side; make exactly one thing an island.
Everything verve.viz draws is SSR SVG. Every verve.anim declarative tween is zero-WASM. Forms degrade through actionForm. That leaves one genuinely interactive concern to spend your stateful chunk on.
Worked example: the showcase page
The showcase composes three subsystems and stays legal:
// 1. A graph computed in Zig, serialized as typed props, revealed on hydrate.
const props = try verve.encodeProps(ctx, islands_reg.VizGraph.Props{
.xs = xs, .ys = ys,
});
const graph = verve.island(ctx, .{ .name = "VizGraph", .props = props },
ctx.div().children(.{ graph_svg })); // SSR SVG is the fallback content
// 2. Declarative entrance animation — no island, runs in the bridge.
section.animate(anim.from(a, ".panel").opacity(0).y(24)
.scrollTrigger(.{ .start = .{ .viewport = .{ .pct = 80 } } }));The VizGraph island is the one stateful chunk; the animation is declarative, so it adds zero chunks. The SVG inside the island tag is the no-JS fallback — it's already correct before hydration, and the chunk only reveals and animates it in.
Data flow, end to end
- SSR computes the real state (
ctx.serverFn-style reads) and renders it — the page is correct and indexable with JS off. - Typed props (
verve.encodePropsagainst a registryPropsstruct) hand structured data to the chunk without a per-chunk JSON parser. The positional codec means field order inislands.zig,core/*, and the chunk must match exactly. - Hydrate wires interactivity: the chunk registers signals, binds
z-on-clickexports, opens an SSE channel if the feature is live. - Round-trips go through server functions (
serverFnPost), whose JSON replies are enveloped{"value": …}— read.valuefirst.
When a feature spans pages
The dashboard shows the other axis: a feature that is several routes. verve.Route.layout wraps child routes in a shared shell; ctx.outlet marks where the child renders; path params (:section), ctx.activeClass for nav state, and .protect(guard) for route guards complete it — all server-side, no island required for the navigation itself.
A checklist for feature pages
- What is the one genuinely interactive thing? That's your island.
- Can the rest be SSR + declarative anim +
actionForm? It almost always can. - Does the page work with JavaScript disabled? If not, the SSR fallback is incomplete.
- Do the
Propsfield orders match across all three copies? - If it's live, is posting a server function and receiving an SSE frame — not the same channel?
Next: Advanced WebGL.