Skip to content

Front modes

Serenade examples are structured as apps and libs under examples/, following an Nx-inspired workspace layout. Each deployable app has a mode (html | spa | ssg | universal) that maps to a dedicated CLI command.

examples/
demo-app/ # bundle + DI + console + RouteLoader skeleton (NOT a front mode)
demo-html/ # mode HTML (server-rendered HTML + CSRF + forms; MyFeed)
demo-monorepo/ # modes SPA + SSG sharing UI lib
apps/
demo-spa/ # Trunk CSR → dist/index.html + wasm + js glue
demo-ssg/ # SSG → per-route HTML + hydrate wasm
libs/
demo-ui/ # shared panels/chrome used by demo-spa and demo-ssg
demo-universal/ # mode Universal (needs a server process)
apps/
demo-front/ # hello Leptos+rangular (ultra-simple shop-shaped front)
demo-back/ # small Serenade listen API (+ serve front assets)
libs/
demo-contract/ # shared serde DTO(s) for GET /hello (and similar)
Path Demoes Does not demo
demo-app Serenade bundle: config packages, Extension, DI service, routes, console commands, event subscriber HTML site, wasm SPA, SSG, product shop
demo-html Mode HTML: server-rendered pages, forms/CSRF, Clitorine/Bootstrap (MyFeed) Leptos, rangular, Trunk dist
demo-monorepo Multi-app front workspace: apps deployable, libs shared; SPA and SSG share UI Backend API, Universal listen
demo-monorepo/apps/demo-spa Mode SPA: hello + button, client-rendered wasm Per-route HTML files on disk
demo-monorepo/apps/demo-ssg Mode SSG: same UI via demo-ui, prerendered HTML per route + hydrate Running Serenade HTTP server
demo-monorepo/libs/demo-ui Shared Leptos UI for spa and ssg Standalone deploy
demo-universal Mode Universal: one process (or documented pair) front+back Full rustashop; flat CDN-only marketing
demo-universal/apps/demo-front Hello wasm UI talking to demo-back; hydrates /cms/hello SSG export
demo-universal/apps/demo-back Boot Serenade kernel, listen, GET /hello (JSON), serve front assets HTML demo (demo-html), console tutorial
demo-universal/libs/demo-contract Shared request/response types front↔back UI components

demo-html is the HTML mode dogfood (server HTML + Clitorine). It is not a Leptos app and does not share demo-ui. Renamed from MyFeed to demo-html to align the folder with the mode name. Package id is now serenade-demo-html; folder name in docs and CLI is demo-html.

Universal needs a backend process. The SPA/SSG monorepo is static/wasm-oriented. Universal lives in its own tree with demo-front + demo-back + demo-contract.

Mode CLI Default app Build artefact
HTML serenade-html examples/demo-html Running server (port bind)
SPA serenade-spa examples/demo-monorepo/apps/demo-spa dist/ shell + wasm + js
SSG serenade-ssg examples/demo-monorepo/apps/demo-ssg dist/ HTML per route + wasm hydrate
Universal serenade-universal examples/demo-universal Running server serving API + front

SSG uses Leptos SSR to render the shared demo-ui component to HTML and Leptos hydration to activate the same UI in the browser. Rangular AOT components lower to Leptos views and follow the same rendering/hydration path; see the rangular SPEC.

All four mode commands use the same selection shape: no app name selects that mode’s default app, an app name selects it explicitly, and --all selects every app registered for that mode. The name argument is useful when a mode has multiple registered apps; currently each mode has one registered demo app, so naming it is equivalent to using the default command.

Mode Default Named app All apps Make
HTML serenade-html serenade-html demo-html serenade-html --all make html / make html-all
SPA serenade-spa serenade-spa demo-spa serenade-spa --all make spa / make spa-all
SSG serenade-ssg serenade-ssg demo-ssg serenade-ssg --all make ssg / make ssg-all
Universal serenade-universal serenade-universal demo-universal serenade-universal --all make universal

HTML runs the app server (default 127.0.0.1:8080); --all starts all selected HTML apps. SPA builds with Trunk to examples/demo-monorepo/apps/demo-spa/dist/. SPA and Universal frontend builds minify generated .html files in dist/ after Trunk. SSG builds a hydration bundle, prerenders the selected app’s declared routes, and minifies the resulting .html files. The serenade-minify component minifies inline classic and module JavaScript as well as HTML, preserving HTML comments (including Leptos hydration markers) and meaningful text whitespace. External scripts and non-JavaScript script data blocks are left intact; CSS is not minified here. Universal builds the demo-front Trunk bundle and starts demo-back on 127.0.0.1:8080; the backend serves /hello JSON, frontend assets, and /cms/hello. The /cms/hello page server-renders the shared Leptos UI and hydrates it in the browser. The demo’s HTTP kernel uses HtmlMinifier for runtime text/html responses. Other server apps can opt in by adding the same middleware to their kernel.

The current HTML and Universal examples both default to 127.0.0.1:8080; running both at once requires changing one app’s bind address.

The per-mode --all flag selects every registered app of that mode for that mode CLI’s normal action. It does not create one combined site or production host.

The planned workspace selector has this contract:

serenade run-many -t build --apps demo-spa,demo-ssg
serenade run-many -t test --apps demo-html,demo-spa
serenade run-many -t build --mode spa
serenade run-many -t build --all-modes

--apps names a comma-separated set of registered apps. --mode selects all apps of one mode; --all-modes selects every app. --apps may be combined with --mode as an intersection filter. A selector is required, conflicting selectors are errors, unknown names and mode mismatches fail explicitly, and empty selections are errors. This run-many command is not implemented yet; use the existing per-mode commands and --all meanwhile.

For hosting, keep build orchestration separate from runtime routing. SPA and SSG outputs are static artifacts and can share an origin when assigned distinct URL prefixes (or use separate domains). HTML and Universal apps are server processes; each needs its own configured bind address, or a reverse proxy must route distinct hostnames/path prefixes to their processes. Their current demo defaults may both use port 8080, so starting multiple server demos together without changing binds will conflict. No production process supervisor, deployment manifest, or multi-app HTTP host is provided by these examples.

When hosting several apps is in scope, add an explicit deployment configuration mapping each app to its mode, public hostname or path prefix, artifact/process, bind address, and health check. Do not infer production routing from Cargo package names or make run-many silently choose ports. Until then, build and deploy each app as its own unit.

Target Effect
make demo Run demo-app (bundle + DI + console skeleton)
make html Run the default HTML-mode app
make html-all Run every registered HTML-mode app
make myfeed Legacy alias for make html
make spa Build demo-spa with Trunk (SPA mode)
make spa-all Build every registered SPA app
make ssg Build and prerender the default SSG app
make ssg-all Build and prerender every registered SSG app
make universal Build the frontend and run the Universal backend
make demo-ssg Build and prerender demo-ssg (SSG mode)
make demo-universal Build the frontend and run demo-universal
Phase Delivers Status
B FRONT-MODES.md · demo-html rename · serenade-html / make html ✅ done
C demo-monorepo · demo-spa · demo-ui · serenade-spa ✅ done
D demo-universal · demo-front · demo-back · demo-contract · serenade-universal ✅ done
E rangular SSR/hydration guidance · demo-ssg · serenade-ssg ✅ done
F All four modes green in CI · hosting boundary and run-many contract documented · handoff current ✅ docs updated; run-many implementation remains future work