Skip to content

SSR Basics

Every isonim-docs page renders through the exact same route manifest and page ViewModel on both the server (nim c, server-side rendering) and the browser (nim js, client-side mount) -- there is no separate SSR-only templating layer.

renderRoute

renderRoute(path, contentDir) is the single SSR entry point: it resolves path against the site's route manifest, loads the matched route's bound content file, builds the page's ViewModel, and renders it to a (status, html) pair.

import ssr

let (status, html) = renderRoute("/guide/ssr-basics")

A path that doesn't match any real route still returns a real result: status 404 and the typed not-found page's HTML, rather than raising.

Why no virtual DOM diffing on the server

The server never mounts a live component tree, so there is nothing to diff against a previous render -- renderRoute builds one HTML string per request straight from the ViewModel, which is also why every dynamic value (page title, body content, link targets) is escaped as it's written rather than trusted as already-safe HTML.

Danger

Never build page HTML by concatenating raw, unescaped content-file text -- always go through the ViewModel/rendering pipeline so link targets and body text get HTML-escaped consistently between the browser and the server.

Route matching and params

docsRouteManifest() is one flat list of typed RouteEntry values -- pattern, canonical path, page kind, and the content file it binds to. matchRoute(manifest, path) walks the list in order and returns the first pattern that matches, with any captured path params alongside it; an unmatched path falls back to the manifest's own typed not-found entry rather than raising. Trailing slashes never need special-casing by an author: /guide/dsl and /guide/dsl/ match the exact same entry, since matching compares non-empty path segments rather than raw strings.

SSR vs. JS mount, side by side

AspectSSR (`nim c`)JS mount (`nim js`)
Entry point`renderRoute``createRouteApp`
OutputAn HTML stringA live DOM node tree
Content loadingReal filesystem readCompile-time embedded content
Route manifestSame `docsRouteManifest()`Same `docsRouteManifest()`

See the ui DSL for the markup layer both entry points render through, and Signals & Effects for why the server render never needs reactivity at all. Back to the site index.

Need some help?
Contact our supportFrequently asked questions