Server-side renderingRender the app to fully-styled HTML on the server for a fast, crawlable first paint — then let the WASM bundle adopt that exact DOM and bring it to life, instead of throwing it away and re-rendering. The page you're reading is served this way.
Render on the serverThe SSR backend is just another `Backend`. It walks the same primitive tree your app produces and emits a complete HTML document — markup, the scoped CSS for every style rule the tree used, and the font links it needs — for any route, on the host, with no browser involved.Because it renders at a path, a complex app with a navigator renders correctly per URL: request `/features/ssr` and you get that screen's document, request `/quickstart` and you get that one. The reactive arena, token registry, and scheduler are thread-local, so each route can render in full isolation with no cross-render contamination.use backend_ssr::{render_path_with, render_document};
// Render the SAME app() the web bundle mounts — at a URL,
// on the host, against the SSR backend.
let page = render_path_with("/features/ssr", register_exts, my_app::app);
let html = render_document(&page, Some("/pkg/app.js"));
// → a complete <html> document: markup + scoped CSS + font links. The same first paintSSR output isn't an approximation of the app — it's the web backend's own first paint, produced by the same style system. The server resolves the same theme tokens, emits the same class names, and lays text out with the same fonts, so the HTML a crawler or a cold browser sees is pixel-for-pixel what the live app would have painted on frame one.That buys you the things a pure client-rendered WASM app gives up: real content in the initial response for search engines and link unfurlers, and a meaningful first paint before a single byte of WASM has executed. The user sees the page; the interactivity catches up.
Hydration by adoptionThe expensive mistake most SSR setups make is re-rendering on the client: the server sends HTML, then the framework boots, ignores that HTML, builds its own tree, and replaces the DOM — a flash and a pile of wasted work. Idealyst hydrates by adoption instead. The booting WASM walks the server-rendered DOM and binds its reactive primitives to the nodes that are already there, in place.Adoption only works if the client agrees with the server about what the tree should be, down to layout. The framework keeps that agreement with viewport-determinism — the client's first measurement matches the server's assumptions — so nodes line up and the bundle can claim the existing DOM rather than rebuild it.
Serving it`serve(...)` stands up an HTTP server that renders the right route per request and returns a fully-styled document. Point `static_dir` at a built web bundle (`idealyst build --web`) and the page also boots that bundle and hydrates; leave it off and you get a pure server-rendered content / SEO preview with no client JS at all.The one rule for matching output: register the same extensions the web build registers. SSR renders identically to the client only when both sides agree on the navigator chrome, the code-block renderer, and any other `Element::External` leaf in the tree.use backend_ssr::{serve, ServeConfig};
serve(
"127.0.0.1:8787",
ServeConfig {
// Built web bundle to boot + hydrate the server DOM:
bundle_module: Some("/pkg/website.js".into()),
static_dir: Some("dist/web".into()), // fonts + bundle
},
register_exts, // same extensions the web build registers
website::app,
)?;
// Each request is SSR-rendered for its route; the bundle then
// boots and hydrates by adopting that document. StatusThe SSR backend is real and in active use: it renders this marketing site — a full DrawerNavigator app — per route, and render-at-path is proven across every page. What you can rely on today is server rendering: HTML + scoped CSS + fonts for any route, matching the web first paint.In-place hydration (the DOM-adoption path above) is a working prototype rather than a turnkey production feature — the demo lives in `examples/hydration-demo` and proves DOM adoption plus viewport-determinism end to end. Expect the ergonomics around wiring it into a scaffolded project to keep firming up.Pairs well with code splitting →