MallaDocs

Rendering and runtime

One renderer for canvas and site, snapshot publishing, islands and the client runtime.

Developer guide

@malla/renderer is a pure function from documents to HTML and CSS. It runs in the canvas frames, in the sites Worker and in tests, which is why the editor matches production.

ts
renderPage({ doc, components, classes, tokens, site, data, user? })
  → { html, css, runtime: RuntimeManifest }

Snapshot publishing

Publish: O(1) in site size
apiPOST /sites/:id/publish checks role and plan.
Snapshotsnapshot.json + compiled shared CSS to R2.
WorkflowsServer workflows to KV per environment.
FlipKV host pointer → new publishId.

A publish writes an immutable snapshot (page and component docs, classes, tokens, SEO, manifest) and flips one pointer, so publishing is instant and atomic, and rolling back is another flip. The publish popover and POST /sites/:siteId/rollback flip it back to one of the environment's 50 most recent publishes (see Roll back); CMS items, assets and other live data are not rolled back. Critical pages are prerendered so they are never cold.

On-demand rendering

A request to a published site
Host lookupKV: host → site, environment, publishId.
AccessPage rule checked per request.
CacheHit: Cache API or R2 write-back.
RenderMiss: renderPage from the snapshot.
  • Static pages are rendered once per publish and cached forever under keys that include the publishId.
  • Server pages run server queries and render per request at the edge, with a short TTL.
  • CMS changes bump a revision per collection; only dependent pages render again.
  • /api/* and /_auth/* go to the runner; /api/_q/* (client queries) and /api/_forms/* stay in sites.

Islands runtime

Published structure is static HTML. Only bound text, attributes, styles and repeated lists change, so @malla/runtime is @vue/reactivity + the shared expression interpreter + a minimal DOM patcher: no virtual DOM, no full hydration, under 25 KB gzipped. It loads only on pages with bindings, forms or client workflows; GSAP is a separate chunk loaded only for interactions.

  • The renderer emits an island manifest of what to hydrate.
  • Per-request pages embed the visitor in the manifest; cached pages fetch /_auth/me once at boot only if something reads user.
  • Multi-page navigation uses Speculation Rules prefetch and View Transitions for an app-like feel.

In the canvas

Each artboard is a sandboxed iframe on a separate origin, talking to the editor over a typed postMessage protocol: init, patch with ops, measure, hit-test. Patches touch only the changed node and its CSS rule.