LumenJS server-renders pages with loaders using @lit-labs/ssr, then hydrates on the client for full interactivity.
When a page has a loader(), LumenJS:
1. Runs the loader on the server
2. Renders the Lit component to HTML with @lit-labs/ssr
3. Embeds the loader data as JSON in the HTML
4. Sends the pre-rendered HTML to the browser
5. The client loads the component JS and hydrates the existing DOM
LumenJS uses Lit's built-in hydration support. The client:
1. Reads the serialized loader data from #__nk_ssr_data__
2. Sets loader data as individual properties on existing DOM elements before loading modules
3. Loads the page module, which triggers Lit's hydration
4. The component attaches to the existing DOM without re-rendering
Nested layouts are also SSR-rendered. Each layout and page is rendered independently, then stitched together as light DOM to avoid hydration mismatches with <slot>.
lumenjs build creates a bundled SSR runtime that includes @lit-labs/ssr and all Lit dependencies. The production server uses this to render pages on every request.
lumenjs build --project ./my-app lumenjs serve --project ./my-app
If SSR fails for any reason, LumenJS falls back gracefully to client-side rendering. The loader data is still injected as JSON. The page just renders on the client instead of being pre-rendered.