re-frame.core¶
re-frame.core is the facade: the single namespace every re-frame2 app requires. It re-exports:
- the registration verbs
- the pipeline verbs
- the view surface
- the effect/interceptor surface
- the frame primitive
- the boot/configure lane
- the instrumentation and registrar-query reads
- every optional feature's registration macro
Core surfaces are documented in full below. Feature surfaces (machines, routing, flows, schemas, SSR, HTTP, resources) are re-exported here so a single import covers everything. Each feature has a brief entry with a pointer to its own namespace doc, which carries the deep contract. For the mental model, see the Subscriptions, Frames, and Effects concept guides.
Registration¶
Every entry here registers a named handler into the frame's registrar.
Return value. Every reg-* returns its primary id: the keyword you registered with (or the path, for reg-app-schema).
reg-event¶
- Kind: macro
- Signature:
- Description: The one public event-registration form. The handler is
(fn [coeffects event-vec] effect-map). It receives a coeffects map and returns a closed effects map{:db ... :fx [...]}, ornilfor a no-op. The db write is an explicit:dbeffect; there is no db-only return shape. - Metadata map (extended form): the optional middle slot carries reflection keys (
:doc,:schema,:tags, …) and a reserved:interceptorsvector:(rf/reg-event :cart/add {:doc "Add an item." :interceptors [undoable]} (fn [{:keys [db]} [_ item]] {:db (update db :items conj item)}))Migration note. The bare positional interceptor middle slot —
(reg-event :id [undoable] handler)— has been removed. Put interceptor chains in the metadata map:(reg-event :id {:interceptors [undoable]} handler). - Raw-context handlers: there is no separate registrar for them. To read or rewrite the interceptor context, register a named interceptor with
(rf/reg-interceptor :my/audit {:before ... :after ...})and reference it by id from the:interceptorsvector ({:interceptors [:my/audit]}). Its:before/:afterfns receive and return the context map directly. (->interceptoris the framework-internal lowering constructor, not the authoring form.) - Example — a pure state update and an effectful handler:
;; State-only: the db write is an explicit :db effect. (rf/reg-event :counter/inc (fn [{:keys [db]} _event] {:db (update db :counter/value inc)})) ;; Effectful: an explicit :db write plus an :fx vector. (rf/reg-event :counter/load (fn [{:keys [db]} _event] {:db (assoc db :status :loading) :fx [[:rf.http/managed {:request {:method :get :url "/api/count"} :on-success [:counter/loaded] :on-failure [:counter/load-failed]}]]}))
reg-sub¶
- Kind: macro
- Signature:
- Description: A computed view over
app-dband other subs. There are three input-production modes (see the table below). A layer-1app-dbreader has no input producer.:<-is a literal producer. A parametricinput-fncomputes inputs from the outerquery-v.
The optional first fn is the v2 input-fn: a pure function from the outer query-v to a vector of query vectors. It must not call subscribe, deref app-db, dispatch, or perform IO. It must not return live reactions either — it is not a v1 reaction-returning signal fn. The runtime resolves each returned query vector in the same frame as the outer subscription.
This is the only sub-registration form in v2; reg-sub-raw is gone (see the migration reference). Full input grammar and error ids: Subscriptions concept guide.
| Mode | Form | Where the inputs come from |
|---|---|---|
| App-db reader | (reg-sub id computation-fn) |
No upstream subs; the computation fn receives app-db and the outer query-v (layer 1). |
| Static inputs | (reg-sub id :<- q1 :<- q2 computation-fn) |
A literal, fixed list of query vectors known at registration (:<- sugar). |
| Parametric inputs | (reg-sub id input-fn computation-fn) |
Computed from the outer query-v by an input-fn when a concrete cache entry is first materialized. |
- Examples:
;; Layer-1 — read straight off app-db (no producer) (rf/reg-sub :counter/value (fn [db _query] (:counter/value db))) ;; Layer-2 — compose an upstream sub via the :<- sugar (static inputs) (rf/reg-sub :counter/doubled :<- [:counter/value] (fn [value _query] (* 2 value))) ;; Parametric — the input-fn returns a vector of query vectors, ;; computed from the outer query-v; the runtime resolves each in the ;; outer sub's frame and hands the resolved values to the computation-fn. (rf/reg-sub :article/page (fn input-fn [[_ article-id]] [[:article/by-id article-id] [:comments/for-article article-id] [:viewer/current]]) (fn computation-fn [[article comments viewer] [_ article-id]] {:id article-id :article article :comments comments :can-edit? (:edit? viewer)}))
reg-fx¶
- Kind: macro
- Signature:
- Description: Define a named side effect. The handler is binary and context-first:
(fn [ctx args] ...). This is a change from v1's unary shape. ctxis a small map carrying:frame(the active frame id),:event(the originating event vector), and:envelope(the parent dispatch envelope).argsis the value the event handler placed beside the fx-id in its:fxvector.- Accepts a
:platformsmetadata key (a set of:server/:client) that gates fx execution by the active platform. See re-frame.ssr.md. - Example:
reg-cofx¶
- Kind: macro
- Signature:
- Description: Register a named supplier for a world fact a handler can ask for. The supplier is a plain value-returning function, not a context-mutating fn:
(fn [] value), or(fn [arg] value)for ids parameterised at the call site. The runtime calls it and puts the result into the coeffects map under the cofx's id. - A handler opts in with
:rf.cofx/requiresregistration metadata. v2 has noinject-cofxinterceptor. - The middle slot carries the fact's grade:
{:recordable? true}— a replayable fact.{:recordable? true :provided? true}— a recordable fact stamped by an owner boundary (no generator).- A bare registration — an ambient (unrecorded) read.
- Reading a sub from a handler works the same way: wrap
subscribe-oncein a cofx and declare it. - Full model: Effects, Coeffects.
- Example:
;; A value-returning supplier — quarantines an impure read behind a named id. (rf/reg-cofx :ui/local-theme {:doc "Ambient localStorage read for the display theme."} (fn [storage-key] (some-> (.-localStorage js/globalThis) (.getItem storage-key)))) ;; The handler declares the fact; the runtime supplies it flat in the coeffects map. (rf/reg-event :prefs/apply-theme {:rf.cofx/requires [[:ui/local-theme "ui-theme"]]} (fn [{:keys [db ui/local-theme]} _] {:db (assoc db :ui/theme (or local-theme "system"))}))
Clearing registrations¶
Each clear-* removes an entry from the registrar; the no-arg form clears the whole kind. Used in tests (the make-reset-runtime-fixture registrar snapshot/restore relies on these), REPL workflows, and teardown.
clear-event¶
- Kind: function
- Signature:
- Description: Forget one event-handler. No-arg clears the whole
:eventregistry. - Example:
clear-sub¶
- Kind: function
- Signature:
- Description: Forget one sub. This clears the registrar side — the inverse of
reg-sub. To decrement the runtime cache instead, useunsubscribe. - Example:
clear-fx¶
- Kind: function
- Signature:
- Description: Forget one fx. No-arg clears the whole
:fxregistry. - Example:
Dispatch and subscribe¶
These are the two verbs that drive the pipeline.
On CLJS, dispatch, dispatch-sync, and subscribe are macros in call position and plain fns in value position (Convention A — the same pattern reg-event / reg-sub / etc. use). In call position the macro form captures the call-site source coords, so tools like Xray can navigate from a trace event back to the originating expression. In value position (an argument, a let-binding, (or dispatch-fn rf/dispatch)) the SAME name resolves to a plain-fn value instead. The fn value composes through higher-order functions like (map dispatch events), where a macro can't sit; that path skips the stamping. Both forms route through the same dispatcher; only the trace stamping differs. There is no *-suffixed twin.
The opts map. dispatch and subscribe accept a uniform opts map: :frame, :fx-overrides, :interceptor-overrides, :trace-id, :source. Target a non-default frame via (rf/dispatch [::save x] {:frame :todo}); the frame id is the public routing address.
dispatch¶
- Kind: macro
- Signature:
- Description: Async dispatch: drops the event onto the frame's queue and returns immediately. This is the default.
-
Example:
-
Fn form: In VALUE position — passed rather than called, e.g.
(run! rf/dispatch events)—dispatchresolves to the plain-fn value instead of expanding as a macro. It skips call-site stamping, so it composes throughmap/comp/partial.optsmay carry:frame(a frame-id keyword or a live frame value). Returnsnil. (A JVM programmatic caller reachesre-frame.router/dispatch!directly.)
dispatch-sync¶
- Kind: macro
- Signature:
- Description: Synchronous dispatch: drains to completion before returning. Use it for tests, REPL workflows, and one-shot app-boot events. Never call it from inside a running event handler; that raises
:rf.error/dispatch-sync-in-handler. -
Example:
-
Fn form: In VALUE position,
dispatch-syncresolves to the plain-fn value. It mirrorsdispatch's value-position forms:optsmay carry:frame(a frame-id keyword or a live frame value).(A JVM programmatic caller reaches;; Fn-form — drive a sequence of events synchronously from runner / test code. (doseq [evec events] (rf/dispatch-sync evec {:frame :app/main}))re-frame.router/dispatch-sync!directly.)
subscribe¶
- Kind: macro
- Signature:
- Description: The reactive handle. It returns a reaction whose value is the registered sub's current output, recomputed when upstreams change. Valid inside views, inside other subs, and (via the cofx wrapper) inside event handlers.
- Target a non-ambient frame via the
{:frame …}opt —(rf/subscribe [:counter/value] {:frame :other});:othermay be a frame-id keyword or a live frame value. - In VALUE position,
subscriberesolves to the plain-fn value (Convention A) for HoF / programmatic reads, with no call-site capture. A JVM programmatic caller reachesre-frame.subs/subscribedirectly. - Example:
subscribe-once¶
- Kind: function
- Signature:
- Description: One-shot read: subscribe, deref, immediately unsubscribe. Returns the current value with no reactive handle retained. Use it in handler bodies and at the REPL — not in views, and not in machine callbacks. A machine
:guard/:action/:entry/:exitMUST NOT callsubscribe-once; it takes host facts as recorded coeffects, including the machines-only{:rf/sub …}source (see re-frame.machines). Target a non-ambient frame via(subscribe-once query-v {:frame f}), mirroringsubscribe. - Example:
unsubscribe¶
- Kind: function
- Signature:
- Description: Decrement the cache ref-count for a query. When the count hits zero, the entry is disposed synchronously (see Subscriptions). The Reagent / UIx / Helix adapters wire it on unmount; most callers never reach for it directly. Target a non-ambient frame with the frame-first
(unsubscribe frame-id query-v)form. Unlikesubscribe/subscribe-once,unsubscribehas no{:frame …}opts form — it is pure teardown, never a hot in-view call. - Example:
clear-sub-cache!¶
- Kind: function
- Signature:
- Description: Dispose every cached entry in a frame's runtime sub-cache and clear the cache. Disposal is synchronous and unconditional. The no-arg form targets the ambient frame and raises
:rf.error/no-frame-contextwhen no scope is established; the one-arg form names the frame. Mostly for tests; rarely needed in app code. - Example:
compute-sub¶
- Kind: function
- Signature:
- Description: The test-friendly companion to
subscribe. It runs the sub graph against a value ofapp-dband returns the result — no cache, no reactivity, no frame. Arguments are query-vector first, db second. dbmay be a bare app-db map, or a full frame-state value ({:rf.db/app … :rf.db/runtime …}) when the graph mixes app-db and runtime-db subs.- JVM-runnable.
- Example:
Standard events (keyword surface)¶
The framework ships a small, fixed set of standard :rf/* events you dispatch like any other. They are framework-owned: the :rf/* single-root namespace is reserved, so re-registering one with reg-event is a loud reserved-id collision (:rf.error/reserved-event-id).
:rf/set-db¶
- Kind: standard event
- Shape:
- Description: The framework-standard
app-dbseeding event. It replaces the wholeapp-dbpartition with the supplied map — a replace, not a merge. It rides the normal post-commit path (schema validation, rollback, trace emission, epoch recording), so seeding is an ordinary traceable event, not a privileged direct write. The handler is pure and returns{:db new-db}, so it touches only theapp-dbpartition, never runtime-db. - Validation: takes exactly one map argument. A missing /
nil/ non-map argument, or any extra trailing arg ([:rf/set-db {} :junk]), throws:rf.error/set-db-bad-value. Emptyapp-dbis[:rf/set-db {}]. - Example:
Views¶
The view layer is substrate-agnostic. The shared dataflow — frames, subscriptions, dispatch, source metadata, registry ids — is uniform across Reagent, UIx, and Helix, and the same capture-frame carry primitive composes across all three. The substrate-specific hooks (use-subscribe, wrap-view, …) live in each adapter's namespace doc (re-frame.adapter.reagent.md / re-frame.adapter.uix.md / re-frame.adapter.helix.md).
reg-view¶
- Kind: macro
- Signature:
- Description: The
defn-shape view registration — the app-facing lane. It auto-defs the symbol, auto-derives the id from(keyword *ns* sym), and auto-injectsdispatch/subscribeas lexical bindings. It rejects non-defn-shape bodies at macroexpand. In all shapes the symbol isdef-ed, so sibling code can write[my-view item]. Render an app-facing view by Var reference or(rf/view id). Bare keyword-tagged hiccup[:my-view "args"]is rejected (see Spec 004 §Resolved decisions). - Example:
reg-view*¶
- Kind: function
- Signature:
- Description: The plain-fn surface beneath
reg-view— the tooling / host lane. No auto-def, no auto-inject, no compile check. Reach for it when: - the id is computed (code-gen pipelines, plugin systems, story scaffolding);
- you don't want a Var (inside a
letor closure); - you are writing a Form-3 component (
(rf/reg-view* :id (r/create-class {...}))— the one app-facing reason to touch the starred form); - you are writing consumer-side library code that registers without imposing a
def.
A reg-view* body has no auto-injected dispatch / subscribe. If the view needs frame-bound dispatch, capture a (rf/capture-frame) at render and use its ops.
- Example:
;; Computed id — the id isn't a literal symbol at the call site.
(defn register-panel! [view-id render-fn]
(rf/reg-view* view-id render-fn))
;; Form-3 — create-class isn't defn-shaped, so it registers through the
;; starred form. Capture the frame at render so lifecycle callbacks dispatch
;; to the captured frame.
(rf/reg-view* :editor/page
(fn [_]
(let [{:keys [dispatch]} (rf/capture-frame)]
(r/create-class
{:component-did-mount (fn [_] (dispatch [:editor/mounted]))
:reagent-render (fn [] [editor-form-view])}))))
view¶
- Kind: function
- Signature:
- Description: Runtime lookup handle. Returns the registered render-fn, not hiccup (
nilwhen the id is not registered). Use it in hiccup as[(rf/view :id) args...]to late-bind a view by id — for a host that resolves a stored view id, plugin-style dispatch, or dynamic chrome. The Var form ([my-view args]) is the app-facing default; this lookup form is for callers who hold the id, not the symbol. - Example:
frame-provider¶
- Kind: Reagent component (SCOPE-only)
- Signature:
- Description: Scopes a React subtree to a frame that already exists; creates / refreshes / destroys nothing. Roots ensure; providers scope — for create-if-absent, use
frame-root. See the frame-provider glossary entry. - Fails loud (
:rf.error/frame-provider-frame-absent) when the named frame is absent.:framemust be a keyword (anil:frame→:rf.error/no-frame-context; a non-keyword →:rf.error/bad-frame-provider-arg). The scope-into-React counterpart towith-frame. - Given an
:id(the ENSURE key), fails loud namingframe-root(:rf.error/frame-provider-given-id). - Example:
frame-root¶
- Kind: Reagent component (ENSURE — a commit-owned two-pass boundary)
- Signature:
- Description: Creates the frame if absent, reuses it if present, and provides its id to descendants. Creation goes via
make-frame, taking the same constructor opts::id/:images/ record-config incl.:initial-events.:idis required and must be a keyword (a missing/non-keyword:id→:rf.error/frame-root-missing-id). - Commit-owned two-pass: the create/seed runs in a client
useLayoutEffect(at commit), not during render — the first render emits no descendant subtree, the frame is created after commit, and only then do the children render against the now-live frame. A render React discards before commit (a Suspense abort, a concurrent tear-off) therefore creates and seeds nothing — no ghost frame. - Reuse does not re-seed: an idempotent re-mount (hot reload, StrictMode dev double-invoke, Story re-eval) preserves durable state and does not replay
:initial-events;:initial-eventsfire once on first successful creation per committed frame-id lifetime. A keyed remount preserves durable state. - There is no destroy-on-unmount — a genuine unmount leaves the frame live. True ownership stays explicit:
make-frame+destroy-frame!inside acreate-class. - A mounted
:id/ opts change fails loud (:rf.error/frame-root-reconfigured) — give the frame-root a Reactkeythat changes to scope a different frame. Given a:frame(the SCOPE key), fails loud namingframe-provider(:rf.error/frame-root-given-frame). - Example:
capture-frame¶
- Kind: function
- Signature:
- Description: Captures the active frame at CREATION time and returns an operation bundle. The bundle's
:dispatch/:dispatch-sync/:subscribeops always target the captured frame. They survive async boundaries —Promise.then,setTimeout, WebSocketonmessage, observer callbacks — where the ambient frame lookup would have unwound. - The handle is locked to one frame: a per-call
:frameopt MUST NOT override it. - It is an operation bundle, not a container — read the frame's app-db value via
(rf/app-db-value (:frame handle)), not the handle itself. - Full async-boundary contract: Frames — the async boundary.
- Example:
make-capture-frame¶
- Kind: function (internal —
:tier :implementation, EP-0024) - Signature:
- Description: Not an app-facing surface — the internal constructor behind
capture-frameand thereg-viewinjection sugar. It is a public Var only so thereg-viewmacro's emitted body can reference it fully-qualified. Callcapture-frameinstead.
with-frame / with-new-frame¶
- Kind: macros (a sibling pair)
- Signatures:
- Description: The two lexical (non-React) frame-scoping macros — for regions that aren't a view tree (tests, the REPL, SSR). Each rejects the other's argument shape at compile time.
with-framepins*current-frame*to an existing frame-id for the dynamic extent ofbody, creating and destroying nothing. The lexical counterpart to therf/frame-provider{:frame …}SCOPE shape.with-new-frameevaluatesexprand binds the resulting frame target tosym— typically the live frame value frommake-frame(a frame value or a keyword id is accepted downstream). It runsbodyin that frame's dynamic context, then destroys the frame on exit. The throwaway-frame form for one-off harnesses.- Example:
;; Pin form — bind *current-frame* to an existing id for the body (most common) (rf/with-frame :todo (rf/dispatch-sync [:todo/add {:text "milk"}])) ;; Eval-bind-run-destroy — a throwaway frame for one test, torn down on exit. (rf/with-new-frame [f (rf/make-frame {:images [todo-image]})] (rf/dispatch-sync [:rf/set-db {:todos []}]) ;; seed via a setup dispatch (rf/dispatch-sync [:todo/add {:text "milk"}]) (is (= 1 (count (:todos (rf/app-db-value f)))))) ;; frame destroyed on exit
Effects and interceptors¶
The effect map is what an event handler returns; the interceptor chain runs before and after the handler. Handlers stay pure (they return descriptions of effects, not the effects themselves), and the runtime actions those descriptions at exactly one point.
The effect map is closed: app handlers return :db + :fx only. (A third reserved top-level key, :rf.db/runtime, exists for framework / runtime-extension authority — never for app handlers.) :db is the new app-db value, replaced in the commit phase. :fx is a vector of [fx-id args] pairs ([fx-id] is the no-args shorthand); the runtime's fx walker runs each pair against the registered reg-fx handler. Both reserved keys are covered by the effect map glossary entry.
reg-interceptor¶
- Kind: macro (with a same-name plain-fn value on CLJS, Convention A — no
*-suffixed twin; a JVM programmatic caller reachesre-frame.interceptor-registry/reg-interceptor*directly) - Signature:
- Description: The public custom-interceptor authoring form. Register a named interceptor descriptor, then reference it by id from a
reg-eventmetadata / frame-config:interceptorsvector. Descriptor shapes: {:before f}/{:after f}/{:before f :after g};{:factory f}for a parameterized family (freceives one arg and returns a descriptor; referenced as[id arg]— the standard[:rf.interceptor/path …]is the canonical factory consumer).
An optional middle slot carries the standard registration-metadata map (:doc, :schema, :tags, …). Use this for any work not covered by the standard interceptors — analytics, logging, validation, ad-hoc context manipulation. (->interceptor is the framework-internal lowering constructor that turns a descriptor into an executable chain entry; it must not appear directly in a public chain.)
- Example:
(rf/reg-interceptor :log-on-error
{:after (fn [ctx]
(when-let [err (:rf.error/last-event ctx)]
(js/console.error err))
ctx)})
(rf/reg-event ::save-cart
{:interceptors [:log-on-error]} ;; reference by id
(fn [cofx _]
{:db (assoc (:db cofx) :cart/saving? true)}))
->interceptor¶
- Kind: macro (internal lowering constructor, EP-0022)
- Signature:
- Description: INTERNAL — not the public authoring form. The framework-internal lowering constructor. It turns a
{:before … :after …}descriptor into an executable chain entry, capturing the definition-site:source-coordfrom(meta &form). Application code registers interceptors withreg-interceptorand references them by id;->interceptormust not appear directly in a public:interceptorschain.
->interceptor*¶
- Kind: function (internal, EP-0022)
- Signature:
- Description: INTERNAL. The plain, runtime-callable fn form of the
->interceptormacro (per Conventions*-suffix naming). HoF / programmatic / REPL callers reach this directly; it captures no source-coord. Not an authoring surface — usereg-interceptor.
with-fx-overrides¶
- Kind: macro
- Signature:
- Description: For the duration of
body, everydispatch/dispatch-syncmerges this fx-overrides map into its envelope. Lexical scope; composes withwith-frame. - The three override scopes compose with a clear precedence: per-call (
(rf/dispatch event {:fx-overrides {...}})) wins, then lexical (with-fx-overrides), then per-frame ((rf/make-frame {:id :todo :fx-overrides {...}})). - At the pattern level the override value is an id. The CLJS reference implementation also accepts a fn value for test wiring. The asymmetry is deliberate: ports that don't ship fn-valued overrides remain pattern-conformant.
- Example:
;; Swap the real managed-HTTP fx for a canned-failure stub for the test body — ;; every dispatch inside inherits the override; it unwinds when the body exits. (rf/with-fx-overrides {:rf.http/managed :auth.login/canned-failure} (rf/dispatch-sync [:auth.login/submit {:email "x@y.z" :password "wrong"}]))
validate-at-boundary-interceptor¶
- Kind: Var (interceptor value) — schemas re-export
- Signature:
- Description: A pre-built interceptor value, not a fn, registered under the framework id
:rf.schema/at-boundary. Reference it by id from areg-eventmetadata map's:interceptorsvector:{:interceptors [:rf.schema/at-boundary]}. Chains are reference-only, so the Var itself never appears as an inline chain entry. - It forces
:schemavalidation of the dispatched event vector even in production builds, where dev-time validation is normally elided. Use it on handlers that ingest data from outside the app's trust boundary: HTTP replies, websocket frames, postMessage. - Do not call it as a fn — invoking it raises
ArityException. - Full contract: re-frame.schemas.md.
- Example:
Standard fx and interceptor (keyword surface)¶
The framework reserves a few :fx entries and one standard interceptor reference. User code registers its own fx-ids via reg-fx. Feature-owned fx ([:rf.http/managed …], [:rf.nav/push-url …], [:rf.machine/spawn …], [:rf.fx/reg-flow …], [:rf.server/* …], …) live in their feature namespace docs.
[fx-id args] |
Args | Status | Intuition |
|---|---|---|---|
[:dispatch event-vec] |
event vector | v1 | Schedule this event on the same queue. Async — runs after the current run completes. |
[:dispatch-later {:ms ms :event event-vec}] |
options map | v1 | Schedule this event after N ms. |
[:rf.interceptor/path <path-vector>]¶
- Kind: interceptor reference (the one standard interceptor)
- Form:
- Description: Focus the handler on an
app-dbsub-slice.:beforestages the focused slice as:db((get-in db path));:afterwidens the returned slice back into full app-db. The handler sees and returns a sub-tree, not the full db. - Preserves the frame-commit
identical?no-op (an unchanged focused slice widens back to the original app-db object, not anassoc-inallocation). - A non-vector / malformed path arg raises
:rf.error/path-interceptor-bad-path. - It is a reference, not a constructed value — there is no public
pathfn (a stalerf/pathcall raises:rf.error/path-removed).
Frames¶
A frame is the scoping unit for app-db, the event queue, and the pipeline. Most apps have exactly one frame, established at the root with rf/frame-root. init! does not create one for you: frame identity is carried, not synthesised from absence (see Frame identity is carried, not found). Apps that need isolation between subsystems register additional frames and dispatch / subscribe against them via {:frame :other}.
make-frame¶
- Kind: function
- Signature:
- Description: The single public constructor for a live frame. It accepts image-selection and frame-configuration options in one call and returns the live frame value: one frame value backed by one registry. Use it for per-mount lifecycles — devcards, modal stacks, multiple live widget instances, dynamic tabs, tests, and the SSR per-request frame pattern.
optsmust be a map — a non-map (includingnil) raises:rf.error/make-frame-bad-opts; a non-vector:imagesraises:rf.error/make-frame-bad-images.- Image-selection opts:
:images— always a vector.:id— optional. Registers the frame in the one process-local live-frame registry. A duplicate live id is idempotent replacement, preserving durable state on re-mount.:adapter.
- Frame-configuration opts (same call):
:initial-events(a vector of event vectors dispatched into the new frame at creation),:fx-overrides,:platform,:ssr,:doc,:preset,:tags. - Pass the value directly — no accessor needed.
dispatch/subscribe/destroy-frame!/app-db-value/frame-providerall accept the frame value OR its id interchangeably. There is no separate value→id accessor to reach for (API-shrink #1, rf2-csbbwu). - The ONE constructor (rf2-h1vqa4 deleted the
reg-framespelling — a frame is a live runtime object, not a registered program member). The day-1 mount recipe isframe-root(ENSURE);make-frameis the programmatic path (tools, tests, SSR, dynamic, image-loaded frames). - The frame config owns the
:observabilitysink policy. Durableapp-dbdata classification is not a frame annotation: a config carrying:sensitive/:largefails loud at registration. To classify durableapp-dbpaths, return the four commit-plane classification effects (:sensitive/:large/:clear-sensitive/:clear-large) from areg-eventalongside:db, wired to run at frame creation via:initial-events— see Keep secrets out of traces. - Lifecycle is the caller's responsibility — pair a direct
make-framewith adestroy-frame!, or use the ENSURE boundaryrf/frame-rootfor view-mounted frames. - See the Frames concept guide and EP-0024.
- Example:
;; Durable app-db classification rides a commit-plane effect (EP-0025): ;; a reg-event returns :sensitive / :large alongside :db, run at frame ;; creation via :initial-events — NOT a frame annotation. (rf/reg-event :app/init (fn [{:keys [db]} _] {:db (assoc db :auth {}) :sensitive [[:auth :token]]})) ;; classify before any value lands (rf/make-frame {:id :app/main :doc "App demo frame." :initial-events [[:app/init]] ;; classifies [:auth :token] at creation :fx-overrides {:rf.http/managed :auth.login.demo/managed-stub}})
Resetting a frame — destroy + make-frame¶
There is no dedicated "reset" function; reset-frame! was retired (rf2-lxwpob). A full replace composes the two lifecycle primitives — no third verb needed. Tear the frame down through the normative destroy-frame! boundary (running :on-destroy, releasing per-feature resources), then re-create it fresh. Machine snapshots, the route slice, flows, and app-db are all rebuilt from the config:
;; Full frame replace (destroy + re-create with the SAME config).
;; Must run OUTSIDE any handler run — e.g. a restart button's :on-click.
(rf/destroy-frame! :app/main)
(rf/make-frame config) ;; re-supply the SAME config (it carries :id :app/main)
- For an image-loaded frame, re-supply the SAME
:imagesvector too — otherwise the recreated frame degrades to the default image generation. - To wipe just the
app-dbpartition while keeping live runtime-db, reach for(replace-frame-state! frame-id {:rf.db/app {}})instead. - There is no
:initial-dbconfig key — seedingapp-dbis itself an ordinary, traceable event,[:rf/set-db {…}]. - Not atomic across a handler-cascade boundary (unlike the retired
reset-frame!).destroy-frame!has no handler-scope guard. Called from inside a handler, it destroys the frame; the followingmake-framethen hits its own construction-in-handler guard and throws, leaving the frame destroyed and not reconstructed. Frame construction/destruction are already top-level/view-only operations, so this only bites a call site that was already violating that rule.
destroy-frame!¶
- Kind: function
- Signature:
- Description: The normative teardown boundary. It claims the exact installed incarnation and atomically cuts ordinary queued work. An authored callback already on the stack may return and entered authored interceptor
:aftercallbacks may unwind, but its returned context/output is inert: no later framework-owned tail or render runs. A configured:on-destroyseed and its same-frame descendants run under the sole private exact-token cleanup exception before lifecycle-dead is published. An external ordinary dispatch in the claim-to-dead window may enter the real queue, but the next exact-incarnation drain check drops it before invocation; dead/absent dispatch and subscribe recover while emitting:rf.error/frame-destroyed. Teardown then releases every frame-scoped feature artefact (flows, machines, schemas, SSR, epoch), clears the sub-cache, and removes the frame. - Example:
current-frame-id¶
- Kind: function
- Signature:
- Description: Return the active frame id the in-effect scope carries: the dynamic
*current-frame*stamp, or a React-contextframe-providerscope (CLJS only). This is the context reader form; it requires an established scope. There is no:rf/defaultfloor. Called under no established scope, it raises:rf.error/no-frame-context(EP-0002).
frame-generation¶
- Kind: function (EP-0023)
- Signature:
- Description: Return the SEALED, resolved image generation a live frame is running: the inert image-assembly generation it resolves
(kind, id)lookups through. This is the raw read over the EP-0023 frame→generation model, for tools (Pair MCPdescribe-image, Xray). frame-targetis a registered frame id or a direct live frame object.- Returns the generation map with stable public keys:
:rf.gen/resolver— the sealed[kind id]map.:rf.gen/imagesand:rf.gen/kinds.:rf.gen/shadows— the cross-image shadow report: what a LATER image overrode in an EARLIER one (EP-0026). A flat vector[{:registration [kind id] :image <defined-in> :shadowed-by <winner>} …];[]when no later image shadowed an earlier one.
- Read the shadow report with
(:rf.gen/shadows (rf/frame-generation f)). There is no dedicated accessor — theframe-shadowsvar was removed (rf2-i4hk4b). - Fails loud (
:rf.error/frame-no-generation) whenframe-targetdoes not resolve to a live frame carrying a generation.
image¶
- Kind: function (EP-0023)
- Signature:
- Description: Construct an image value — a selected registration-set value, as inert data (EP-0023 / EP-0026). Pure — no registrar, no side effect. The result is the assembled registration set a frame resolves against (passed to
make-frame/frame-providerunder:images).speccarries exactly three public keys: :id(optional);:select-ns(an{:include [globs] :exclude [globs]}selection map over registered descriptors' provenance namespaces);:registrations(inline registrar-keyed sections).
Image hot-reload — re-make-frame¶
There is no dedicated "reload" function; reload-images! was folded into re-construction (rf2-lxwpob). To re-seal a frame's (kind, id) resolver after handler/sub/view source changes, re-call make-frame against the SAME :id with a NEW :images vector. Frame memory is preserved: the constructor's surgical-update path swaps only the generation, while app-db, runtime-db, caches, and lifecycle continue unchanged:
- Composition-replacing: it replaces the whole
:imagesvector, not one member; a non-vector raises:rf.error/make-frame-bad-images. - Frame-targeted: the swap affects only the named frame's generation, not siblings sharing an image.
- To read the diff the old
reload-images!report carried, callframe-generationbefore and after the call and diff the two values withgeneration-diff:
(let [before (rf/frame-generation :my/frame)
_ (rf/make-frame {:id :my/frame :images [new-image]})
after (rf/frame-generation :my/frame)]
(rf/generation-diff before after))
;; => {:added #{[kind id] …} :changed #{…} :removed #{…} :retained #{…}}
generation-diff¶
- Kind: function (EP-0023)
- Signature:
- Description: A PURE diff between two sealed image generations. It replaces the report the retired
reload-images!verb used to return: readframe-generationbefore and after a re-make-framereload, then diff the two. - Returns
{:added #{[kind id] …} :changed #{…} :removed #{…} :retained #{…}}.
Lifecycle and configure¶
The surfaces that bring a re-frame2 process up and take it down. The one-line boot sentence: install an adapter with init!, then create your frame(s) explicitly. The adapter-author surfaces (install / dispose / inspect) sit one layer down; an ordinary app never touches them.
init!¶
- Kind: function
- Signature:
- Description: The idempotent boot. Required arg: the adapter spec map. Each adapter ns exports an
adapterVar; require the ns and pass the Var, e.g.(rf/init! reagent-adapter/adapter). (init!)with no args raisesArityExceptionat compile / load time;(init! nil)or(init! :reagent)raises:rf.error/no-adapter-specifiedat runtime.- Installs adapters and runtime capabilities only; does not create or guarantee any frame — mount your app frame at the root with
frame-root(ENSURE), or construct it explicitly withmake-frame. - Example:
init-platform¶
- Kind: function
- Signature:
- Description: Set the host-wide active-platform marker. The runtime tracks the active platform so
reg-fx/reg-cofx:platformsmetadata can gate execution. - CLJS hosts default to
:client, JVM hosts to:server; call this at boot to override (e.g. a CLJS-on-Node SSR runtime sets:server; a JVM-runnable browser-simulating test sets:client). - Anything other than
:server/:clientraises:rf.error/invalid-platform. Idempotent / re-callable. - Per-frame
:config :platform(set by the:ssr-serverpreset) is the finer-grained alternative and wins over the host-wide marker. - Example:
install-adapter!¶
- Kind: function
- Signature:
- Description: Must be called before any frame is created. Lower-level than
init!; ordinary apps callinit!instead. It installs once: a second call without an interveningdestroy-adapter!raises:rf.error/adapter-already-installed. Use it for a custom boot pipeline with steps between adapter-install and first-frame creation. - Example:
destroy-adapter!¶
- Kind: function
- Signature:
- Description: Tear down the exact installed adapter generation. This is a one-way terminal boundary: it attempts all adapter-owned cleanup, preserves and rethrows the primary failure with later failures retained as diagnostic evidence, and finally clears only the generation it claimed.
adapter-disposed?remains true even when cleanup throws. A fresh adapter may install afterward; stale finalization never clears a replacement. Symmetric withinstall-adapter!and withdestroy-frame!. - Example:
current-adapter¶
- Kind: function
- Signature:
- Description: Which substrate is installed. Answers
:rf.adapter/reagent/:rf.adapter/reagent-slim/:rf.adapter/ui/:rf.adapter/uix/:rf.adapter/helix/:rf.adapter/plain-atom/:rf.adapter/ssr/:custom— ornilwhen no adapter is installed. For predicate / branch code. - Example:
current-adapter-spec¶
- Kind: function
- Signature:
- Description: The adapter spec map passed to
(rf/init! ...), ornilwhen no adapter is installed. For tools / routing / identity checks across the install / dispose lifecycle. For the discriminator keyword, usecurrent-adapter. - Example:
adapter-disposed?¶
- Kind: function
- Signature:
- Description: Returns
trueiff terminal teardown of the most recent installed adapter generation has been claimed and no subsequentinstall-adapter!has fired. Cleanup success is not implied: it remains true when destruction rethrows a cleanup failure.falsefor never-installed (fresh process) AND after a fresh install. Read-only. Use to distinguish:rf.error/no-adapter-installed(fresh process) from:rf.error/adapter-disposed(torn down). - Example:
configure!¶
- Kind: function
- Signature:
- Description: Process-level data knobs, typically set once at boot. It is one of three orthogonal configuration surfaces:
configure!— process-level data knobs;- the
set-!/install-!setters — adapter-pluggable hooks; - per-frame metadata — frame-scoped overrides.
The key vocabulary is closed-and-additive: existing keys cannot be renamed, and new keys are added by extending the table. Three keys ship:
| Key | Opts | Default | Status | What it tunes |
|---|---|---|---|---|
:epoch-history |
{:depth N :trace-events-keep N :redact-fn fn} |
{:depth 50, :trace-events-keep 50, :redact-fn nil} |
v1 (dev-only) | Per-frame epoch ring depth, trace-event retention cap per record, and an optional projection-side redactor applied only at off-box egress (inside projected-record). |
:trace-buffer |
{:events-retained N} |
{:events-retained 50} |
v1 (dev-only) | The dev-only per-frame trace ring's event-slot count: one slot per event, regardless of how many trace events its run emitted. 0 disables retention (the surface stays live). |
:elision |
{:rf.size/threshold-bytes N} |
{:rf.size/threshold-bytes 16384} |
v1 | The size threshold above which elide-wire-value substitutes a :rf.size/large-elided marker. 0 disables runtime auto-detect. |
There is no :sub-cache knob: sub-cache disposal happens synchronously when the derefer count hits 0. SSR error-projection policy (:public-error-id, :dev-error-detail?) is not a configure! key; it is per-frame metadata on the frame's :ssr map. Framework-owned semantic sub-keys use a namespaced keyword (:rf.size/threshold-bytes); ergonomic per-knob sub-keys are unqualified (:depth, :trace-events-keep, :redact-fn).
- Example:
(rf/configure! {:epoch-history {:depth 100}
:trace-buffer {:events-retained 25}
:elision {:rf.size/threshold-bytes 8192}})
feature-loaded?¶
- Kind: function
- Signature:
- Description: Is the named optional feature's implementation artefact on the classpath? Detection is a pure keyword lookup in the always-loaded feature registry (no exception, no classpath probe). Probe
(feature-loaded? :routing)before taking a feature-dependent path. - Example:
features¶
- Kind: function
- Signature:
- Description: Return a map of every optional feature keyword to its inspection entry: the feature's static coordinate data (
:maven/:require/:spec) merged with its live:loaded?status. - Example:
require-feature!¶
- Kind: function
- Signature:
- Description: Assert the optional feature is loaded. Returns
truewhen its implementation artefact is on the classpath. When it is not, throws a structured:rf.error/feature-not-loadedex-info carrying the exact copy-pasteable Maven coordinate + require form. An unknown feature keyword throws:rf.error/unknown-feature. Use as an early guard at the top of code that depends on an optional feature. - Example:
Instrumentation and listeners¶
Two surfaces stacked. The first is dev-only: a trace bus that emits one richly-tagged record per noteworthy event. Records are buffered into a ring and fanned out to registered listeners synchronously; the whole surface is elided under :advanced + goog.DEBUG=false. The second is always-on: tight, production-survivable substrates (event-emit, error-emit) that deliver one record per processed event and one per :rf.error/* event. The epoch (time-travel) surfaces are dev-only and also available natively as re-frame.epoch.md. The complete error catalogue is normative in Spec 009.
register-listener!¶
- Kind: function
- Signature:
- Description: Register
callback-fnunderidto receive every record the runtime emits onstream. Delivery is synchronous: the callback returns before the next record. Re-registering the same id on a stream replaces. - Streams:
:trace— dev-only, DCE'd in production.:eventsand:errors— always-on; they survive CLJS:advanced+goog.DEBUG=false.:epoch— optional artefact.
- Returns
id(nilon the:epochstream when theday8/re-frame2-epochartefact is absent). An unknownstreamthrows:rf.error/unknown-listener-stream. - Example:
;; Dev-only: tap every trace event the runtime emits (DCE'd in production). (rf/register-listener! :trace :my-app/trace-tap (fn [trace-event] (js/console.log (:op-type trace-event) (:operation trace-event)))) ;; Always-on: one record per processed event → a hosted metrics back-end. (rf/register-listener! :events :my-app.monitors/datadog (fn [{:keys [event-id frame outcome elapsed-ms]}] (datadog/timing "rf.event.elapsed_ms" elapsed-ms {:event (str event-id) :frame (str frame) :outcome (str outcome)}))) ;; Always-on production error monitoring — the payload is a union; branch on (:error record). (rf/register-listener! :errors :my-app.monitors/sentry (fn [record] (if-let [ex (:exception record)] (Sentry/captureException ex) (Sentry/captureMessage (str (:error record))))))
unregister-listener!¶
- Kind: function
- Signature:
- Description: The inverse — drop one listener registered under
idonstream. - Example:
There is deliberately no facade clear-listeners! verb. Dropping every listener on a stream is a test-isolation concern owned by the fixture layer. re-frame.test-support's reset clears the registries through the lower-level sinks directly: re-frame.trace.tooling/clear-listeners!, re-frame.event-emit/clear-event-listeners!, re-frame.error-emit/clear-error-listeners!, and the :epoch/clear-epoch-listeners! reset hook. The former per-stream clear-listeners! façade verb was retired in API-shrink #4.
emit-trace-event!¶
- Kind: function
- Signature:
- Description: Emit a custom trace event. Use sparingly — the framework emits the load-bearing events; custom emission is for app-specific cross-cutting concerns the framework can't know about.
- Example:
trace-buffer¶
- Kind: function
- Signature:
- Description: Read the named frame's dev-only, event-keyed ring non-destructively. By default it returns one bundle per retained pipeline run;
{:flat true}returns the raw trace events instead. - Returns
[]for a destroyed / never-registered frame, and[]in production (the ring is never allocated). - On the
re-frame.corefacade this is a JVM-only alias — CLJS callers usere-frame.trace.tooling/trace-bufferdirectly. - The retained event-slot count is the
(rf/configure! {:trace-buffer {:events-retained N}})knob. - Example:
clear-trace-buffer!¶
- Kind: function
- Signature:
- Description: Empty the named frame's ring. No-op for an unknown frame, no-op in production. On the
re-frame.corefacade this is a JVM-only alias — CLJS callers usere-frame.trace.tooling/clear-trace-buffer!directly. - Example:
group-by-event¶
- Kind: function
- Signature:
- Description: A pure data projection. It turns a list of trace events into per-event records
{:dispatch-id :parent-dispatch-id :frame :event :dispatched :handler :fx :effects :subs :renders :other}— one record per[frame dispatch-id]pipeline run, sorted by emission order. JVM-runnable. - Example:
group-by-event-with-events¶
- Kind: function
- Signature:
- Description: Like
group-by-event, but each record additionally carries a:trace-eventsslot holding the vector of raw trace events that composed that event's run. The same[frame dispatch-id]grouping is reused verbatim; the:trace-eventsslot is the exact set of events the record was reduced from, in input order.
domino-bucket¶
- Kind: function
- Signature:
- Description: Classify a raw trace event into the pipeline-stage slot used by
group-by-event. Pure. (The "domino" name is the first-contact mnemonic for those stages.) - Example:
elide-wire-value¶
- Kind: function
- Signature:
- Description: The framework primitive that walks tree-shaped values at the wire boundary and substitutes elision markers for sensitive or large slots. It is the single normative emission site for the
:rf/redactedsentinel and the:rf.size/large-elidedmarker. It walksvconsulting the frame's runtime-db classification declarations; the frame resolves from the explicit:frameopt, else from the carried scope. - Redaction is strictly path-based: a secret re-keyed off its classified path ships raw. That is the fail-open default; to redact it, classify the destination path.
- A live frame carrying no declarations passes the value through unchanged. A frameless or unresolvable-frame call fails closed: the whole value is redacted to
:rf/redacted. Opt out with:rf.size/include-sensitive? true. - This is the low-level value walker; the record-level boundary primitive is
project-egress. - Example:
project-egress¶
- Kind: function
- Signature:
- Description: The public, record-level boundary primitive — the required step before any off-box sink. It dispatches on a record's
:kind(:rf.observe/handled-event/:rf.observe/error) to a per-kind projector, and falls back to walking a kindless input as a tree-shaped value. Each tree-shaped slot is delegated toelide-wire-valueagainst the frame's classification. optscarries:rf.egress/profile(the closed six-member enum),:frame,:path, and advanced:rf.size/*overrides.- An unknown profile throws
:rf.error/unknown-egress-profile. - Fail-closed: a tree slot projects only when the frame is known — no
:rf/defaultsynthesis. - Full model: Keep secrets out of traces.
- Example:
;; Verify what an off-box sink will receive before wiring it. (rf/project-egress {:kind :rf.observe/handled-event :frame :app/main :event-id :auth/sign-in :event [:auth/sign-in {:password "hunter2"}]} {:rf.egress/profile :rf.egress/off-box-observability}) ;; => {:kind :rf.observe/handled-event :frame :app/main :event-id :auth/sign-in ...} ;; no :event off-box
register-observability-sink!¶
- Kind: function
- Signature:
- Description: Register an observability sink fn
funder the keywordsink-id— the id a frame's:observability {:handled-events [{:sink <sink-id> :rf.egress/profile …}]}entry names. freceives a single already-projected record (projected under the owning frame's classification and the entry's egress profile); it does no sink-local redaction.- Re-registering the same id replaces. Returns
sink-id. - Always-on — survives CLJS
:advanced+goog.DEBUG=false. - This is the production-normal observability seam: frame-scoped and profile-projected. It parallels the corpus-wide
register-listener!surface, which is cross-frame and raw. - Example:
(rf/make-frame {:id :app/main :observability {:handled-events [{:sink :my-app.sinks/datadog :rf.egress/profile :rf.egress/off-box-observability}]}}) (rf/register-observability-sink! :my-app.sinks/datadog (fn [projected-record] ;; already projected — no sink-local redaction (datadog/send projected-record)))
unregister-observability-sink!¶
- Kind: function
- Signature:
- Description: Drop the observability sink registered under
sink-id. Returns nil. - Example:
sensitive?¶
- Kind: function
- Signature:
- Description: True iff
trace-eventis a map carrying:sensitive? trueat the top level (not under:tags). The framework-published predicate every consumer composes against. - Example:
epoch-history¶
- Kind: function (dev-only; also
re-frame.epoch/epoch-history) - Signature:
- Description: Per-frame epoch snapshots, recorded on each drain-completion in dev builds. Returns
[]for an unknown / destroyed frame. Used by pair-shaped tools for time-travel and post-mortem analysis. Production builds elide entirely. See re-frame.epoch.md. - Example:
restore-epoch!¶
- Kind: function (dev-only; also
re-frame.epoch/restore-epoch!) - Signature:
- Description: Restore the frame's whole frame-state — both the app-db and runtime-db partitions — to the named epoch's
:frame-state-after. The state is reinstalled in one atomic write, so machine snapshots, the route slice, and other runtime-db material rewind alongside app-db. Returnstrueon success. Returnsfalsefor an unknown / destroyed frame, and emits:rf.error/no-such-handlerof kind:frame. - Example:
replace-frame-state!¶
- Kind: function (dev-only; also
re-frame.epoch/replace-frame-state!) - Signature:
- Description: The ONE frame-state write surface. (API-shrink #3, rf2-t3lftq, consolidated the former
replace-app-db!/reset-app-db!/replace-runtime-db!/replace-frame-state!four-mutator family into this.)frame-stateis a PARTIAL frame-state map: any subset of{:rf.db/app … :rf.db/runtime …}. A present key replaces that partition; an absent key is preserved unchanged. It bypasses the dispatch loop and records a synthetic epoch sorestore-epoch!can rewind. A map carrying no recognized partition key, or an unrecognized key, is rejected as:rf.error/replace-frame-state-bad-keys(checked before frame resolution). Returnstrueon success,falseon a documented failure. - Examples:
;; App-only state injection (the former replace-app-db!). (rf/replace-frame-state! :app/main {:rf.db/app {:counter 0}}) ;; App-only reset to {} while runtime-db survives (the former reset-app-db!). (rf/replace-frame-state! :app/main {:rf.db/app {}}) ;; Runtime-only injection — app-db untouched (the former replace-runtime-db!). (rf/replace-frame-state! :app/main {:rf.db/runtime new-runtime-db}) ;; Full-frame install — both partitions atomically. (rf/replace-frame-state! :app/main {:rf.db/app new-db :rf.db/runtime new-runtime-db})
Epoch-settled listeners¶
Epoch drain-settle listeners are the :epoch stream of the stream-parameterized listener verb. There is no separate facade register-epoch-listener! fn — the per-channel pair was retired in API-shrink #4. The epoch stream registers through the one verb exactly like :trace / :events / :errors.
- Signature:
(rf/register-listener! :epoch key callback-fn)/(rf/unregister-listener! :epoch key) - Description: Process-global assembled-epoch listener, dev-only. The callback fires once per drain-settle with the assembled
:rf/epoch-record; re-registering the samekeyreplaces. A callback whose previously-observed frame is destroyed receives a one-shot:rf.epoch.cb/silenced-on-frame-destroytrace. Returnskey, ornilwhen theday8/re-frame2-epochartefact is absent. - Example:
projected-record¶
- Kind: function (dev-only; also
re-frame.epoch/projected-record) - Signature:
- Description: Project an
:rf/epoch-recordfor off-box egress. It routes the record through the egress projection, applying the optional(rf/configure! {:epoch-history {:redact-fn …}})redactor, so an epoch record can be shipped off-box safely. The ring and listeners always deliver the raw record, so projection never affectsrestore-epoch!fidelity. See re-frame.epoch.md.
projected-history¶
- Kind: function (dev-only; also
re-frame.epoch/projected-history) - Signature:
- Description: Return the projected vector of records for a frame (each member projected as by
projected-record). The off-box-safe companion toepoch-history.
Registrar queries¶
The registrar holds every registered handler — events, subs, fx, cofx, flows, machines, views, schemas — as a queryable data structure, which is what makes the framework's tools possible. This is the read-side surface; the write-side is reg-* / clear-* above. Everything here is JVM-runnable.
registrations¶
- Kind: function
- Signature:
- Description: Walk the registrar, returning the full metadata map per id: source-coords,
:rf/sensitive,:rf/machine?,:platforms, the doc string. Use it when you want metadata; the optionalpred-fnfilters by the metadata map. - The frame-targeted form
(registrations {:frame :tenants/acme :kind :sub})returns only the ids that frame's image carries, resolved through the frame's sealed image generation (EP-0023). An optional:predfilters as above. - A map argument is always a frame-targeted read: a map without
:frameraises:rf.error/registrar-query-needs-frame; a:framethat does not resolve to a live frame carrying a generation raises:rf.error/frame-no-generation. - Example:
handler-meta¶
- Kind: function
- Signature:
- Description: What
reg-*stamped at this id. View registrations include source-coord keys (:ns/:line/:column/:file); pair tools resolvedata-rf2-source-coordDOM annotations to:filevia this lookup. - Registrar kinds:
:event,:sub,:fx,:cofx,:interceptor,:view,:frame,:route,:head,:error-projector,:flow,:resource. - The two machine kinds
:machine-guard/:machine-actionare additionally accepted on the positional arity with a 2-vector id:(handler-meta :machine-guard [machine-id guard-id]). They are derived on demand from the machine's registration spec (a dev-only source; not frame-targetable). - The map arity is the frame-targeted read; the same map-arity errors as
registrationsapply (:rf.error/registrar-query-needs-frame,:rf.error/frame-no-generation). - App-db schemas are not a registrar kind — look them up via
(app-schema-meta-at path)in re-frame.schemas.md. - Example:
frame-ids¶
- Kind: function
- Signature:
- Description: Return the set of all live (non-destroyed) frame ids. The optional prefix (a string) filters by namespace.
- Example:
frame-meta¶
- Kind: function
- Signature:
- Description: What
make-framestamped at this frame. Returns the (post-preset-expansion) metadata map::fx-overrides,:interceptors,:ssr,:on-error, schema bindings. - Example:
app-db-value¶
- Kind: function
- Signature:
- Description: Return the current
app-dbvalue for this frame: the deref'dapp-dbmap — a plain value, not the container. Returnsnilfor an unknown / destroyed frame. Accepts a frame-id keyword or a live frame object. - Example:
frame-state-value¶
- Kind: function
- Signature:
- Description: Return the coherent frame-state projection for the named frame:
{:rf.db/app <app-db> :rf.db/runtime <runtime-db>}, ornilfor an unknown / destroyed frame. This is the full-frame read for SSR / epoch / time-travel / Xray. A fresh frame's state is{:rf.db/app {} :rf.db/runtime {}}. For a runtime-db-only read, use(:rf.db/runtime (frame-state-value frame-id))— the dedicatedruntime-db-valuewas retired (rf2-t3lftq, API-shrink #3). - Example:
snapshot-ofwas retired by rf2-t3lftq (API-shrink #3); it was an empirically zero-caller convenience overapp-db-value+get-in. Use(get-in (rf/app-db-value frame-id) path)directly.
Feature registration (re-exports)¶
These surfaces are re-exported on the re-frame.core facade so a single import covers everything. Each is defined in full — signature, metadata grammar, examples — in its feature namespace doc. Entries here are brief; author against the linked doc. Each feature's keyword surfaces (its :fx ids, standard events, and standard subs) also live in the feature doc.
Machines → re-frame.machines.md¶
A state machine is registered with one call and is an event handler; the transition table is data. Keyword surfaces ([:rf/machine machine-id] sub, [:rf.machine/spawn …] / [:rf.machine/destroy …] / [:rf.machine/dispatch-to-system …] / [:raise …] fx) live in the machines doc.
reg-machine¶
- Kind: macro
- Signature:
(reg-machine machine-id ?metadata machine-spec) - The canonical machine-registration macro. It compiles a transition-table spec into a
reg-eventhandler, co-locating per-element source coords for Xray navigation. The optional middle slot is a registration-metadata map; its:schemavalidates the dispatched outer event vector. Full contract in re-frame.machines.md.
defmachine¶
- Kind: macro
- Signature:
(defmachine name ?docstring machine-spec) - The
def-shape companion toreg-machine. It defines the machine-spec value and bindsname, capturing per-element source at the definition site so a later(reg-machine :id name)retains it. (The plain-fnreg-machine*is not on the facade — it lives inre-frame.machines.) Full contract in re-frame.machines.md.
Read a machine's snapshot with the ordinary subscribe, naming its framework sub vector. @(rf/subscribe [:rf/machine machine-id]) returns the whole {:state :data :tags} snapshot. @(rf/subscribe [:rf.machine/has-tag? machine-id tag]) is a reactive :tags-membership predicate. Full contract in re-frame.machines.md.
Routing → re-frame.routing.md¶
Routes are data; the current route lives in runtime-db (read via the :rf/route sub). Keyword surfaces (:rf.route/navigate, :rf.route/transitioned, :rf.route/url-requested events; [:rf.nav/push-url …] / [:rf.nav/replace-url …] / [:rf.nav/scroll …] fx; the :rf/route sub family) live in the routing doc.
reg-route¶
- Kind: macro
- Signature:
(reg-route id metadata path) - Register a route as data. The id is the dispatch target; the path is the URL shape; the metadata carries match events and guards (
:on-match,:can-leave,:params,:query, …). Full contract in re-frame.routing.md.
route-link¶
- Kind: registered view (function)
- Signature:
[rf/route-link {:to :route-id :params {...} :query {...} :fragment "..."} & children] - A registered view at
:route/link. It renders an<a href=...>from a route id and intercepts plain primary-button clicks to dispatch:rf.route/url-requested. Modifier-key / middle clicks and:target/:downloadanchors defer to the browser. Full contract in re-frame.routing.md.
There is no install-url-listener! / remove-url-listener! (or install-history-listener! / remove-history-listener!) on this facade. The browser URL-change listener is wired automatically by the :url-bound? frame lifecycle: creation installs it, destroy removes it. See re-frame.routing.md § Browser URL listener.
Flows → re-frame.flows.md¶
A flow is derived state: declared inputs (frame-state paths), a pure :derive, and an app-db :output-path the runtime recomputes on input change. The runtime-registration fx ([:rf.fx/reg-flow …] / [:rf.fx/clear-flow …]) and clear-flow live in the flows doc.
reg-flow¶
- Kind: macro
- Signature:
- Register a flow in the canonical 3-slot grammar:
flow-idfirst, the purederive-fnlast, andmetadatacarrying:inputs/:output-path(both required) plus optional:doc/:schema/ the:framemounting key. Returnsflow-id. Full contract in re-frame.flows.md.
Schemas → re-frame.schemas.md¶
Malli schemas attached to app-db paths; validated on writes in dev, elided in production. The introspection surface (app-schemas, app-schema-at, …) and validator-extension seams live in the schemas doc. (validate-at-boundary-interceptor is rowed above under Effects and interceptors.)
reg-app-schema¶
- Kind: macro
- Signature:
- Attach this Malli schema to this
app-dbpath. The schema is the positional value slot; the optional middle metadata map carries the:frametarget (rf2-qm7k83 Part A). Path is the registration id — the onlyreg-*that is path-keyed rather than id-keyed. Full contract in re-frame.schemas.md.
reg-app-schemas¶
- Kind: macro
- Signature:
- The bulk plural form. It registers many path→schema entries against the active frame (or the
:frameopt) in one call; each entry is stamped with this call's source coords. Returns the vector of paths registered. Full contract in re-frame.schemas.md.
SSR → re-frame.ssr.md¶
Server-side rendering is the same framework server-side. A curated set of rendering and head primitives is re-exported here as late-bound wrappers. Each wrapper resolves to the re-frame.ssr implementation when the day8/re-frame2-ssr artefact is on the classpath, and throws a clear "SSR not loaded" error otherwise. The host-adapter surface (re-frame.ssr.ring.md) and the per-request :rf.server/* fx are not re-exported. Standard SSR events (:rf/server-init, :rf/hydrate), subs (:rf/head, :rf/public-error), and the server-only fx live in the SSR doc.
reg-head¶
- Kind: macro
- Signature:
(reg-head id ?metadata head-fn) - Register a head-fn
(fn [db route] head-model)keyed by id; routes opt in via:headroute metadata. Full contract in re-frame.ssr.md.
reg-error-projector¶
- Kind: macro
- Signature:
(reg-error-projector id ?metadata projector-fn) - Register a trace-event → public-error projector
(fn [trace-event] :rf/public-error), named per-frame via the frame's:ssr {:public-error-id …}metadata. Full contract in re-frame.ssr.md.
render-to-string¶
- Kind: function
- Signature:
- The canonical server-side render: it walks the hiccup tree once and emits a string.
optsmay carry:doctype?and:emit-hash?. JVM-runnable; pure. Full contract in re-frame.ssr.md.
render-tree-hash¶
- Kind: function
- Signature:
(render-tree-hash render-tree) → 32-bit FNV-1a structural hash (lowercase hex) - A deterministic structural fingerprint of a render tree (same canonical-EDN → same hash on JVM and CLJS); used by the hydration compatibility check. Full contract in re-frame.ssr.md.
project-error¶
- Kind: function
- Signature:
(project-error frame-id trace-event) → :rf/public-error - Apply the active error-projector (selected by the frame's
:ssr {:public-error-id …}metadata) — the seam between an internal error trace event and a client-safe public-error projection. Full contract in re-frame.ssr.md.
render-head¶
- Kind: function
- Signature:
(render-head head-id opts) → :rf/head-model - Evaluate the registered head-fn for
head-id, returning a head-model. Full contract in re-frame.ssr.md.
active-head¶
- Kind: function
- Signature:
(active-head frame-id) → :rf/head-model - Resolve the head-model for the currently active route in the named frame. This is a frame-scoped read: the frame is carried, not ambient, and a
nilframe-id raises:rf.error/no-frame-context. Full contract in re-frame.ssr.md.
head-model->html¶
- Kind: function
- Signature:
- Render a head-model to its inner-head HTML string (
:wrap?controls whether<head>tags are emitted; default false). Full contract in re-frame.ssr.md.
head-snapshot¶
- Kind: function
- Signature:
(head-snapshot frame-id) → {head-id → :rf/head-model} - Read the per-frame snapshot of last-produced head-models (
{}for a frame that has never seen arender-headcall). Full contract in re-frame.ssr.md.
HTTP → re-frame.http.md¶
Managed HTTP is an optional capability: one fx-id ([:rf.http/managed …]), one args map, one closed failure taxonomy. The verb helpers (rf.http/get, post, …), the [:rf.http/managed …] / [:rf.http/managed-abort …] fx, the failure taxonomy, and the raw install/uninstall stub pair live in the HTTP doc.
reg-http-interceptor¶
- Kind: macro
- Signature:
(reg-http-interceptor id interceptor-map) - Register an HTTP interceptor on a frame's
:rf.http/managedmiddleware chain.:before (fn [ctx] ctx')runs request-side, in registration order.:after (fn [ctx response] response')runs response-side, in reverse order. Full contract in re-frame.http.md.
clear-http-interceptor¶
- Kind: function
- Signature:
- Unregister an HTTP interceptor by id (single-arity resolves the frame from carried scope; two-arity names it). Full contract in re-frame.http.md.
with-managed-request-stubs¶
- Kind: macro
- Signature:
(with-managed-request-stubs route-map body+) - Lexical-scope HTTP stubbing.
route-mapis{[<method> <url>] {:reply {:ok v}}}(or{:failure …}). Inside the body, matching requests bypass the real client and auto-route by method + URL, with no manual:fx-overrides. Full contract in re-frame.http.md.
with-managed-request-stubs*¶
- Kind: function
- Signature:
(with-managed-request-stubs* route-map body-fn) - The plain-fn surface beneath the macro — for computed route-maps or non-literal bodies. Full contract in re-frame.http.md.
Resources → re-frame.resources.md¶
Resources are an optional capability (cached server-state reads plus mutations) on the re-frame.core facade. The events and subscriptions are keyword-addressed ([:rf.resource/ensure …], [:rf.resource/refetch …], [:rf.mutation/execute …], the passive :rf.resource/* / :rf.mutation/* subs, …) and live in the resources doc.
reg-resource¶
- Kind: macro
- Signature:
(reg-resource resource-id metadata request-fn) - Register a resource as data.
metadatacarries the required fail-closed:scopepolicy plus:params-schema; therequest-fnreturns a managed-HTTP args map. Full contract in re-frame.resources.md.
reg-mutation¶
- Kind: macro
- Signature:
(reg-mutation mutation-id metadata request-fn) - Register a mutation — the causal-write counterpart of a resource: a named write that, on success, invalidates / patches / populates cached reads. Full contract in re-frame.resources.md.
reg-resource-scope¶
- Kind: macro
- Signature:
(reg-resource-scope scope-id metadata resolve-fn) - Register a named resource-scope resolver under
scope-id; a resource's:scopepolicy references it. The canonical 3-slot grammar applies: the:resolvefn is the value slot, andmetadatacarries the declared:inputs(omit:inputsfor the 2-arg whole-db sugar). Returnsscope-id. Full contract in re-frame.resources.md.
clear-resource¶
- Kind: function
- Signature:
(clear-resource resource-id) - Remove a registered resource (a registration-lifecycle operation — NOT cache invalidation) and dispose its per-frame runtime state. Returns
resource-id. Full contract in re-frame.resources.md.
clear-mutation¶
- Kind: function
- Signature:
(clear-mutation mutation-id) - Remove a registered mutation (registration-lifecycle — NOT the
[:rf.mutation/clear …]runtime-instance reset). Returnsmutation-id. Full contract in re-frame.resources.md.
clear-resource-scope¶
- Kind: function
- Signature:
(clear-resource-scope scope-id) - Remove a registered resource-scope resolver (registration-lifecycle). Full contract in re-frame.resources.md.
resolve-resource-scope¶
- Kind: function
- Signature:
(resolve-resource-scope db scope-id) - Resolver helper: resolve the named resource-scope resolver against
db. Full contract in re-frame.resources.md.
resource-meta¶
- Kind: function
- Signature:
(resource-meta resource-id) → spec-map or nil - Tool/test lane: project the registered resource's spec (
:params-schema,:request,:scope,:stale-after-ms,:tags, source coords). Full contract in re-frame.resources.md.
resource-state¶
- Kind: function
- Signature:
(resource-state {:resource … :scope … :params … :frame …}) → entry or nil - Tool/test lane: a resource instance's durable runtime entry at an explicit frame (resolves the scoped key as a subscription would). Full contract in re-frame.resources.md.
resources¶
- Kind: function
- Signature:
- Tool/test lane: resource introspection for a frame — the static registry plus, with
:frame, the live per-frame instance entries. Full contract in re-frame.resources.md.
mutation-meta¶
- Kind: function
- Signature:
(mutation-meta mutation-id) → spec-map or nil - Tool/test lane: the registered mutation's spec (
:request,:params-schema,:invalidates,:patches,:populates,:scope, source coords). Full contract in re-frame.resources.md.
mutation-state¶
- Kind: function
- Signature:
(mutation-state {:instance … :frame …}) → row or nil - Tool/test lane: a mutation instance's durable runtime row (
{:status :result :error …}) at an explicit frame. Full contract in re-frame.resources.md.
mutations¶
- Kind: function
- Signature:
- Tool/test lane: mutation introspection for a frame — the registered ids plus, with
:frame, the live per-frame instance table. Full contract in re-frame.resources.md.
install-revalidation-listeners!¶
- Kind: function (CLJS-only)
- Signature:
(install-revalidation-listeners! frame-id) → nil - Wire three host
windowlisteners forframe-id—focus/visibilitychange→[:rf.resource/window-focused]andonline→[:rf.resource/network-reconnected]. Idempotent (hot-reload safe). Full contract in re-frame.resources.md.
remove-revalidation-listeners!¶
- Kind: function (CLJS-only)
- Signature:
(remove-revalidation-listeners! frame-id) → nil - Tear down the revalidation listeners installed by
install-revalidation-listeners!(a no-op when none is installed). Full contract in re-frame.resources.md.
See also¶
- Subscriptions, Frames, Effects, Coeffects, Observability — the concept guides behind these surfaces.
- Boot and mount an app, Keep secrets out of traces — the working guides.
- Core glossary — the surface vocabulary in one place.
- The feature namespace docs linked under Feature registration (re-exports) carry the deep contract for every re-exported macro and its keyword surfaces.