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.

setup event

The app's own event that boots a per-request frame, named in :initial-events. These docs call it :app/server-init; any app-namespaced id works. The app registers it, 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; 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). hydrate! reads and installs it.

client frame id

ssr-handler's :client-frame-id option: a stable frame id, such as :rf/default, 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 adopt.

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 adopt.

hydration mismatch

The client's first render disagreeing with the server's HTML. When React recovers from one while a root hydrates — differing text, or a missing, extra or wrong-type element — it re-renders that part on the client, and the root reports the recovery as a :rf.ssr/hydration-mismatch trace carrying React's message, plus an always-on error record. An attribute-only difference is not reported: React's development console.error is the only signal. CI catches a mismatch through the root's :on-recoverable-error callback. 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, so updating the live head after a client-side route change is 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 nil leaves that error to the default projector; one 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.

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.