Skip to content

API reference

Every public name Fresco ships, grouped by namespace. Six namespaces have their own pages in the API reference: re-frame.fresco, re-frame.fresco.forms, re-frame.fresco.overlay, re-frame.fresco.motion, re-frame.fresco.native and re-frame.fresco.substrate. For those six, this page is an index: each name links its API entry, which states the contract, and the chapter that teaches it. The server, tool, evidence and test-kit namespaces have no API page, so their sections here are the reference itself.

How to read an entry

An indexed namespace opens with a sentence saying what it is and the alias this guide requires it under. Its names follow in tables with one row per public name: the name, its API entry, and the chapter that teaches it.

A reference namespace opens with one block carrying every name it exports, then a table saying what each one is for. A name written (name args) is called; a name written [name props children] is a Hiccup head; a name with no parentheses is a value. Each entry states what the name takes, what it returns, and the few facts a signature cannot show, and links the chapter that teaches it.

Errors are ex-infos carrying a stable :rf.error/… id in ex-data, except evidence/envelope's refusal, which carries none. Entries, here and on the API pages, name the ids a name raises; Troubleshooting describes the shape every error carries and indexes each id.

Two questions are answered on other pages:

Question Where it is answered
What each surface does on the server, and under hydration SSR and hydration
Every error id, its cause and its fix Troubleshooting, with the error shape above it

re-frame.fresco — the main namespace

The one namespace an ordinary application requires. Each optional module is a separate namespace, so an application that never requires one carries none of its code.

(ns my.app
  (:require [re-frame.fresco :as h]))

Authoring

Name Reference Taught in
h/defview defview Views and reads
h/defhost defhost Interop
h/event event Events as data

Reads

Name Reference Taught in
h/sub sub Views and reads

The frame functions a body calls are core's, (rf/current-frame-id) and (rf/capture-frame); see Events as data.

Roots

Name Reference Taught in
h/client-root client-root Installation
h/render! render! Installation
h/unmount! unmount! Installation

A first h/render! that does not hydrate renders inside flushSync, so it returns with the tree already in the DOM, a frame-root's seeded children included.

The frame is written in the tree

Every re-frame2 view adapter uses these two heads; Frame boundaries says which one goes where.

Name Reference Taught in
h/frame-root frame-root Installation
h/frame-provider frame-provider Installation

Hydrating roots

A hydrating root scopes a frame that already holds the server's state with h/frame-provider, and adopts the server's DOM through (h/render! … {:hydrate? true}). The render! entry states the rules, and SSR and hydration teaches the whole route.

Markup

Name Reference Taught in
h/error-boundary error-boundary Errors
h/portal portal Interop
h/route-link route-link Routing and navigation
h/as-element as-element Interop
h/as-component as-component Interop

Local state

Name Reference Taught in
h/reg-state reg-state Ephemeral state

The marker keywords

These are keywords in the re-frame.fresco namespace, written ::h/… once the namespace is aliased as h.

Name Reference Taught in
::h/value Marker keywords Events as data
::h/checked Marker keywords Events as data
::h/prevent Marker keywords Events as data
::h/revision Marker keywords Controlled inputs
::h/clear Marker keywords Ephemeral state

The presence markers ::motion/mounting and ::motion/unmounting belong to re-frame.fresco.motion.

re-frame.fresco.forms

The optional forms module: a buffered text field and the h/reg-state concern its drafts live under.

(ns my.app
  (:require [re-frame.fresco.forms :as forms]))
Name Reference Taught in
forms/buffered-field buffered-field Forms
forms/drafts drafts Forms

A test names the field's three events through re-frame.fresco.test.forms.

re-frame.fresco.overlay

The optional overlay module: two heads that put their content on the browser's native top layer.

(ns my.app
  (:require [re-frame.fresco.overlay :as overlay]))
Name Reference Taught in
overlay/modal modal Overlays and focus
overlay/popover popover Overlays and focus

The options the two heads take, the attributes the module writes itself and the ids they raise are listed under The heads.

re-frame.fresco.motion

The optional motion module: one head that keeps exiting children on screen long enough for a CSS exit transition to run.

(ns my.app
  (:require [re-frame.fresco.motion :as motion]))
Name Reference Taught in
motion/presence presence Motion and presence
::motion/mounting, ::motion/unmounting presence Motion and presence

re-frame.fresco.native

The two hooks a React island uses to reach re-frame2 state. An island is a React or UIx component mounted through h/defhost.

(ns my.app
  (:require [re-frame.fresco.native :as n]))
Name Reference Taught in
n/use-frame use-frame Islands
n/use-sub use-sub Islands

re-frame.fresco.server

The optional server module: renders one request to an HTML document with Node's react-dom/server.

(ns app.server
  (:require [re-frame.fresco.server :as server]))

(server/render opts)
(server/render-body opts)
(server/payload-script payload-edn)
(server/document {:html h :payload-script s :app-element-id id
                  :script-src src :title t})

server/render renders one request and returns:

{:frame-id       :the-per-request-gensym   ;; already destroyed when render returns; for test assertions
 :html           "the app root's INNER markup"
 :payload        {}      ;; the :rf/hydration-payload map
 :payload-edn    "that map, pr-str'd"
 :payload-script "<script …>"
 :document       "the whole page"}

Its opts:

Key Meaning
:hiccup required. The root Hiccup form
:payload required. Which app-db state is sent to the client: a non-empty vector of top-level app-db keys, or :rf.ssr.payload/whole-app-db. Omitting it raises :rf.error/ssr-missing-payload-policy
:snapshot a map seeded whole through :rf/set-db
:initial-events ordinary events, run after the snapshot
:client-frame-id the stable wire :rf/frame-id, or absent to omit the key
:identifier-prefix React's identifierPrefix. The hydrating root must be handed the same string
:app-element-id, :script-src, :title the document envelope's
:frame-opts extra rf/make-frame options for the request frame, such as :images, :url-strategy or :fx-overrides. :id, :platform (always :server) and :initial-events belong to this module and cannot be overridden
:version, :schema-digest passed to the payload builder
:payload-include-sensitive optional vector of app-db paths classified :sensitive whose raw value may ride the payload (a CSRF token, say). Absent, every classified value arrives as :rf/redacted

If the runtime recorded an error during the render, even one it recovered from, server/render raises :rf.error/ssr-render-failed instead of returning markup the application did not mean to render.

server/render-body renders only the app root's inner markup, with no payload or document. It is for a host whose JVM side has already run the request frame and its boot events. opts takes :hiccup and :render-state (both required; :render-state is the {:rf/app-db … :rf/runtime-db …} map the JVM produced), plus :identifier-prefix and :frame-opts as above. It runs no :initial-events, seeds no :snapshot, and returns the HTML string. A render that recorded an error raises as render does.

The other two help a host that post-processes server/render's result. server/payload-script takes an EDN string, such as (pr-str payload), and returns the <script id="__rf_payload" type="application/edn"> tag with EDN-aware script escaping. Pass the serialized string, not the payload map.

server/document returns a complete HTML string around :html and the already-built :payload-script. It emits lang="en", a UTF-8 meta tag and a root <div> whose id is :app-element-id (default "app", also when nil). :title adds an escaped title; :script-src adds the bootstrap script after the payload. Both are omitted when absent. Use your own shell when the page needs a different language or additional head content.

The determinism check lives in the test kit as re-frame.fresco.test.server/render-twice.

Taught in SSR and hydration.

re-frame.fresco.substrate

Fresco's own adapter, the value an application passes to rf/init! before it mounts anything.

(ns my.app
  (:require [re-frame.fresco.substrate :as substrate]))
Name Reference Taught in
substrate/adapter adapter Installation

re-frame.fresco.tool

The four reads Xray and AI tooling use to inspect Fresco's runtime. Each takes no argument and returns nil in a production build.

(ns my.tooling
  (:require [re-frame.fresco.tool :as tool]))

(tool/read-mounted-boundaries)
(tool/read-read-attribution)
(tool/read-intents)
(tool/explain-render)
Name The question it answers
tool/read-mounted-boundaries which views are mounted and reading subscriptions right now, in which frames
tool/read-read-attribution which views read each subscription. The only read here that is always exact
tool/read-intents what was dispatched inside the runtime's retained event window, oldest first
tool/explain-render which subscription values changed, and which views read them

Each returns an envelope carrying :schema, :producer, :read, :complete? and :loss; check :complete? and :loss before trusting the contents. tool/explain-render cannot link a render to the event that caused it, so it reports {:reason :uncorrelated} and offers :candidates as leads. In a development build each row also carries :views: the defviews mounted there, each with its source location, or :unknown for an unnamed body. Taught in Diagnostics.

re-frame.fresco.evidence

The evidence vocabulary and the one function every envelope goes through. A consumer reads this namespace; a producer calls it.

(ns my.tooling
  (:require [re-frame.fresco.evidence :as evidence]))

;; identity and vocabulary
evidence/schema  evidence/producer  evidence/reads
evidence/unknown evidence/loss-reasons

;; the constructor
(evidence/envelope read complete? loss body)
Name (evidence/…) What it is
schema :re-frame.fresco.evidence/v3, the schema version every envelope carries. A consumer checks it first and rejects a version it does not recognise; older shapes are not accepted
producer :re-frame/fresco, which substrate produced the envelope. The schema is adapter-neutral, so this is carried rather than inferred
reads :mounted-boundaries, :read-attribution, :intents, :explain-render: the allowed :read values
unknown :unknown, the explicit value for a fact a read does not hold, and the :dropped count for a loss it cannot size. Unknown is never encoded as an empty collection
loss-reasons why a read could not carry something: :cap, :opaque, :host-opaque, :uncorrelated. Each names a different remedy
envelope returns body with the five envelope fields overwritten by the validated stamp. complete? must be a boolean; loss is nil or {:reason reason :dropped n}, where n is a non-negative integer or :unknown. A loss cannot accompany true

Invalid envelope arguments throw ex-info with {:re-frame.fresco.evidence/defect :incoherent-envelope :problems [...]}. :problems lists every validation failure; this exception does not use an :rf.error/id.

re-frame.fresco.test — the L1 and L2 test kit

Ships from test_kit/src: it is in the jar but not on the artefact's :paths, so a :local/root consumer adds that root to its test classpath explicitly. Nothing here mounts anything; these tiers inspect values and run view bodies.

(ns my.app-test
  (:require [re-frame.fresco.test :as ht]))

;; the testing ladder, as data
ht/ladder

;; L1 — what a value is
(ht/boundary? v)   (ht/host? v)   (ht/callback? v)
(ht/view-name v)   (ht/host-policy v)

;; L1 — what the codec does with one form
(ht/element-props form)
(ht/controlled? form)
(ht/revision form)
(ht/materialize intent-v {:value v :checked c})
(ht/canonical-dom node)
(ht/capture-intents frame-kw f)
(ht/fire! frame-kw form prop event)

;; L2 — the structural tree
(ht/tree form)
(ht/tree form {:subs fixtures})
ht/tree-version
(ht/find-all tree pred)   (ht/find tree pred)
(ht/attrs node)           (ht/text node)      (ht/intents tree)
(ht/role node)            (ht/accessible-name tree node)
(ht/unnamed-controls tree)
Name What it answers
ht/boundary? is v a view, the value h/defview defines? False for the plain function its body is
ht/host? is v a host, the value h/defhost defines? False for the foreign component it wraps
ht/callback? is v the one callback form? False for an identically written plain fn
ht/view-name the "<ns>/<sym>" name a view or host carries; nil for anything else
ht/host-policy the :server policy a crossing was declared with. Anything that is not a defhost value raises :rf.error/fresco-test-not-a-host rather than answering nil
ht/ladder the testing ladder as data — five rows, L0 to L4, each with :tier, :proves, :mechanism and :here? (whether this namespace covers that tier)
ht/element-props the emitted prop slots of one native form, as a map of slot name to value. A converted handler records as {:rf.ui/opaque :fn}
ht/controlled? does the codec install the controlled shadow for this form? The runtime's own decision, not a re-derivation
ht/revision the ::h/revision value a native form carries, as the runtime reads it
ht/materialize what an event vector becomes at dispatch, given the target's value and checked flag, as a pure function
ht/canonical-dom a DOM subtree serialised with attribute names sorted and the development annotations data-rf2-source-coord and data-rf-view removed
ht/capture-intents runs f and returns {:value <f's value> :intents [event-v …]} — the events dispatched into frame-kw meanwhile. Other frames' events are ignored
ht/fire! converts one handler position to its React callback and invokes it with an event described as data; returns {:intents […] :prevented? bool}
ht/tree runs one hook-free body under injected read fixtures and returns its versioned semantic tree. opts takes only :subs; any other key throws
ht/tree-version the structural-tree schema version ht/tree stamps on its root
ht/find-all / ht/find every node, or the first node, for which pred is truthy, in document order. nil threads through a missed match
ht/attrs the merged attribute projection of a node — :attrs with :events for an element, the passed props for a boundary call, {} for a fragment
ht/text the concatenation of a node's text descendants. Over a boundary node this is what the call site wrote, never the child's own rendering
ht/intents every event vector the tree carries, in document order — what a rendering offers to dispatch, where ht/capture-intents says what it did
ht/role the ARIA role of a node — written, else implicit — as a keyword, or nil
ht/accessible-name the accessible name a node carries within a tree. A node not in the tree throws rather than returning nil
ht/unnamed-controls every operable node with no accessible name, in document order. It does not exempt a control inside an aria-hidden subtree

ht/fire! takes a native Hiccup form, a handler prop such as :on-input, and an event map with :value, :checked, :key, :composing? and :key-code. The last two default to false and 0. With h aliased to re-frame.fresco, the following uses real dispatch into an existing frame, so registered handlers can change app-db:

(ht/fire! :app [:input {:on-input [:todo/draft-edited ::h/value]}]
          :on-input {:value "Buy milk"})
;; => {:intents [[:todo/draft-edited "Buy milk"]] :prevented? false}

This checks handler semantics, not browser defaults, bubbling, focus or IME interaction. Use mounted/browser tests for those claims.

ht/tree is not a renderer: no React element is created, no hook runs, and nothing is mounted or painted. It throws on a h/defhost component, a raw React element or an unforced delay anywhere in the tree, and on a read no fixture answers. Taught in Testing.

re-frame.fresco.test.mounted — the L3 test kit

The mounted tier: a real React root, a real frame and a real DOM.

(ns my.app-test
  (:require [re-frame.fresco.test.mounted :as hm]))

(hm/mount! form)
(hm/mount! form {:initial-events es :images imgs :container node :clock true})
(hm/hydrate! form)
(hm/hydrate! form {:html bytes :container node :initial-events es :images imgs :clock true})
(hm/hydrate! form opts budget-ms)

(hm/rerender! handle form)
(hm/dispatch-and-settle! handle event)
(hm/settle! handle)
(hm/settle-until! handle pred)
(hm/settle-until! handle pred {:label "what is being waited for"})
(hm/advance-clock! handle ms)

(hm/unmount! handle)
(hm/residue handle)
(hm/assert-clean! handle)

(hm/census)
(hm/bodies-run f)
hm/counted
hm/this-frame
(hm/shadow! opts)
Name What it does
hm/mount! mounts form on a fresh React root under a frame of this mount's own, and returns the handle. :initial-events seeds that frame in core's own vocabulary; :images is the image composition that frame is built from, passed to rf/make-frame as given (omit it for the default image); :container renders into an element you already have; :clock true installs a virtual clock before anything else this call does. Any other key throws :rf.error/fresco-test-bad-option, naming the accepted keys
hm/hydrate! mounts by adopting server bytes, and returns a promise of the handle, resolved once this root's adoption window has shut. :html supplies the bytes, or :container a container you already filled; :initial-events and :images are as for hm/mount!; :clock true is installed once adoption has finished, not before it. Any other key rejects with :rf.error/fresco-test-bad-option. The default budget is 3000 ms, and an adoption that outruns it rejects with :rf.error/poll-until-timeout
hm/rerender! renders form into the existing root — same root, same frame, same DOM nodes wherever React can keep them
hm/dispatch-and-settle! dispatches into this mount's frame through the runtime's own synchronous dispatch, drains it, commits the echo, and returns the handle
hm/settle! lets everything React has already scheduled commit. The empty flushSync, with no work of its own — it cannot reach work that is merely enqueued
hm/settle-until! waits for pred, settles once, and returns a promise of the same handle. opts is core's poll-until options — :timeout-ms (default 2000), :interval-ms, :label — and a timeout rejects with :rf.error/poll-until-timeout. Use it for work a router has enqueued rather than scheduled
hm/advance-clock! moves this mount's virtual clock forward and runs what falls due. Throws :rf.error/fresco-test-bad-option without {:clock true}, because an advance with no clock under it would assert nothing
hm/unmount! tears the root down and touches nothing the runtime holds, which is what lets hm/assert-clean! see what leaked. The container stays in the document, emptied
hm/residue a promise of the report — :clean?, :leaked, :baseline, :now and the frame — asserting nothing and resetting nothing
hm/assert-clean! waits for quiescence, compares against this mount's baseline, reports through cljs.test/do-report, and only then resets. It never throws, so the promise never rejects
hm/census everything the facade counts as one map: the five residue counters plus :frames, a set of live frame ids, so a delta names the frame that outlived the mount
hm/bodies-run how many boundary bodies ran while f did — what a change cost, where the census says what the page retains
hm/counted [:cells :cell-refs :boundaries :edges :entries], the five residue counters in report order
hm/this-frame the stand-in each mount's own frame keyword normalises to in a shadow report, so a difference is never merely the two mounts being two mounts
hm/shadow! mounts a reference and a candidate against isolated copies of one seeded frame, drives both with one script, and compares canonical DOM and the intent stream at every checkpoint. opts carries :reference, :candidate, :initial-events, :script and, optionally, :images, the composition both frames are built from (any other key throws), and a script step is {:click selector} or {:type [selector text]}, in order. Returns {:status :green :checkpoints n}, or a red naming the checkpoint

The residue counters cover Fresco's runtime. They do not count arbitrary DOM listeners, browser timers or handles retained by foreign libraries.

Green from hm/shadow! means the two implementations were indistinguishable for the flows in the script, and proves nothing about a path the script did not walk.

re-frame.fresco.test.forms and re-frame.fresco.test.server

Two small kit namespaces, each kept separate so that a test requires only what it uses.

Name What it is
tf/edit-id, tf/commit-id, tf/cancel-id the event ids of forms/buffered-field's protocol — [edit-id control revision text], [commit-id control revision on-commit], [cancel-id control revision on-cancel] — for a test that drives the field by hand or asserts on its intents
ts/render-twice runs server/render twice on the same opts and compares the two documents byte-for-byte. Returns {:first :second :identical? :differs-at}, where :differs-at is the index of the first differing character, or nil. The only kit namespace that requires the server module and react-dom/server