Skip to content

SSR glossary

Terms used by re-frame2's server-side rendering, one entry each, with a link to the page that teaches it.

SSR

Server-side rendering: rendering your app to an HTML string on the server, per request, so the first paint arrives before the client bundle runs. In re-frame2 the same events, subscriptions and views run on both sides; code that belongs to one side is declared with platform gating. See The model.

per-request frame

The frame the host creates for one HTTP request and destroys when the response is written. Its :initial-events boot the page's state, and the runtime drains them to a settled app-db before rendering. Concurrent requests each have their own frame, so they cannot see each other's state. See Tutorial Step 2.

:rf/server-init

The conventional boot event for a per-request frame, named in :initial-events. The name is reserved and your app registers the handler, usually with :platforms #{:server} and :rf.cofx/requires [:rf.server/request] so it can read the request. It dispatches whatever the page needs, typically the route change for the request's URL. See Reading the request.

render-to-string

ssr/render-to-string: a pure function from hiccup to an HTML string, with no browser or DOM, runnable on the JVM. It lives only in re-frame.ssr; there is no copy on the rf/ facade. See render-to-string.

blocking resource

A resource a route declares with :blocking? true in its :resources. The Ring handler waits for a route's blocking resources to settle before it renders, within a 5-second render budget; one that has not settled by then enters :error, and the page renders its error state. Nothing else is waited for: a fetch an :initial-events handler starts itself is usually still in flight when the render begins. See Data the first render needs.

Ring handler

What day8/re-frame2-ssr-ring provides. ssr-handler returns a plain Ring handler that runs one request through a per-request frame and returns a Ring response map; stream-handler is the same with a streamed body; ssr-middleware sends the requests its :match? predicate accepts (every GET by default) to an ssr-handler and everything else to the handler it wraps, such as static assets and API routes. Request keys that Ring middleware adds, such as :form-params (wrap-params), :session (wrap-session) and :cookies (wrap-cookies), are on the request only if that middleware runs in front. Options: re-frame.ssr.ring.

page shell

The HTML document around the rendered body: doctype, <head>, the app element (id="app"), the __rf_payload script and the bootstrap <script src="/main.js">. default-html-shell builds it; the :head, :body-end, :script-src (false for none) and :app-element-id options adjust it, and :html-shell replaces it for ssr-handler. :head and :body-end are inserted without escaping, so never build them from untrusted input. See default-html-shell.

payload allowlist

The Ring handler's required :payload option: the top-level app-db keys allowed to reach the client. Every key not named stays on the server, including keys added later. Omitting the option throws :rf.error/ssr-missing-payload-policy when the handler is constructed. See :payload.

hydration payload

The server's finished state, written into the page as EDN in <script id="__rf_payload">: :rf/version, :rf/app-db (filtered by the payload allowlist), :rf/runtime-db (the framework's serialisable slice: route and machine snapshots) and :rf/render-hash. hydrate! reads and installs it.

client frame id

ssr-handler's :client-frame-id option: a stable frame id, such as :app, written into the payload as :rf/frame-id so the client can check it is hydrating the frame the server meant. It is omitted by default, because the server renders under a per-request frame the client never sees. Never set it to a per-request value: a payload whose id differs from the client's :frame throws :rf.error/hydration-frame-id-mismatch. See Hydrate, then verify.

hydration

The client adopting the server's rendered page instead of discarding it and rendering again. ssr/hydrate! installs the hydration payload by dispatching the framework-owned :rf/hydrate before the first render, replacing the client frame's state rather than merging into it; the adapter's render! with {:hydrate? true} then attaches to the existing DOM. See Hydrate, then verify.

render hash

A structural hash of the server's render tree, stamped as data-rf-render-hash on the root element and as :rf/render-hash in the payload; hydrate! hashes the client's first render and compares the two. It ships only for a fn-form :root-view whose root view returns an element. See What the hash covers.

hydration mismatch

The client's first render disagreeing with the server's HTML. For views that return hiccup, hydrate! compares the two render hashes and emits a :rf.ssr/hydration-mismatch trace carrying both. By default the client's render replaces the server's; with :ssr {:on-mismatch :hard-error} on the client frame it throws, which is how CI catches one. See When the renders disagree.

deploy-drift checks

Two checks :rf/hydrate runs on the client: the payload's :rf/version against the client's SSR protocol version (:rf.ssr/version-mismatch), and, when the server was given a :schema-digest, the payload's :rf/schema-digest against the client's registered schemas (:rf.ssr/schema-digest-mismatch). Each emits a trace and lets hydration proceed. They catch a server and a client bundle from different deploys. See Deploy-drift checks.

several roots on one page

A page whose server markup is adopted by more than one client root, such as a header and a cart, usually hydrating one frame. ssr/hydrate-page! boots them in order, each inside its own failure boundary with its optional :mount-fn, so a root that throws is reported (:rf.error/root-boot-failed) and the others still boot. Installing the same payload twice into one frame does nothing; a different payload throws :rf.error/frame-payload-conflict. See Several roots on one page.

platform gating

The :platforms declaration on an effect or coeffect. One registered as #{:client} (or #{:server}) is skipped when a drain runs on the other side, with a :rf.fx/skipped-on-platform trace (:rf.cofx/skipped-on-platform for a coeffect), so a handler runs on both platforms without branching on the runtime. Events are not gated. See :platforms.

head model

The data that becomes <title>, <meta>, OpenGraph and JSON-LD on the server-rendered page: a pure (db, route) → head-model function registered with reg-head and named by a route. The runtime ships no DOM-head reconciler and compares only the body hash, so updating the live head after a client-side route change, and checking the head's :rf/head-hash, are the app's or host's job. See Head metadata.

error projector

The pure function that maps an internal trace to the client-safe :rf/public-error shape when a server render throws. The error page receives only the projected shape, so internal detail never reaches the response. The shape is closed: exactly :status, :code, :message and :retryable?. A projector that returns anything else is discarded in favour of the fixed generic 500. See When the server throws and reg-error-projector.

error view

The page ssr-handler sends when a request's error projects to a 5xx: :error-view, a registered view id or a (fn [public-error] …) returning hiccup, rendered from the public error alone. A projected 4xx keeps the app's own page. Failures the projector cannot see, such as the per-request frame failing to set up or a header that cannot be written, go to :on-error, a (fn [request throwable] …) returning a Ring response, whose default is a fixed plain-text 500. See When the server throws.

suspense boundary

The streaming component ssr/boundary: an :id, a :fallback and a subtree. On the server it defers the subtree to a later chunk; in the browser it renders the subtree, so one view serves both. The server sends the page shell with fallbacks in place, then each boundary's subtree as its data resolves. A boundary that throws keeps its fallback and emits a trace; the rest of the page streams on. :rf/suspense-boundary is internal wire syntax, never written by hand. See Streaming.

hydration delta

In a streamed page, the app-db changes a suspense boundary's drain made, sent with that boundary's chunk and filtered through the same :payload allowlist, so the region's subscriptions see the right state when it swaps in. The final chunk carries the full payload, and where the two disagree the payload wins. See Streaming.

Node renderer

re-frame.ssr.ring.node/renderer, passed as ssr-handler's :renderer, renders the page body on a Node sidecar process (implementation/ssr-node) instead of on the JVM. It is for a native view layer, such as Fresco, whose components are JavaScript the JVM cannot call. The JVM keeps everything else: the frame, the drain, the head, the payload and the response. See Render on Node.

render-state policy

The Node renderer's required :render-state option: which top-level app-db and runtime-db keys the sidecar receives for rendering, or a (fn [frame-id] …) that projects them. It is a separate allowlist from :payload, because what the render needs and what the browser may see can differ, such as a server-only notice the render reads but the browser never receives. See Two policies.