re-frame.fresco¶
re-frame.fresco is the public door of Fresco, the re-frame-native view layer.
Everything an author writes against lives here; everything below it is
re-frame.fresco.impl.* and is not a consumer surface. The intended spelling is
one alias:
Fresco replaces the view notation and nothing else. Events, app-db,
subscriptions and effects are unchanged — read re-frame.core
for the pipeline and this page for the authoring surface.
This page is the manifest-tracked index of the door's public vars: Kind,
Signature, and what the var is. The full contract — the four shapes an on-*
prop may take, the defhost options table, the mount! / hydrate! asymmetry,
the render discipline — lives in the Fresco API reference,
alongside the guide that teaches it. This page deliberately does not duplicate it;
where an entry below is terse, that reference is where the depth is.
A split-host namespace. The door is a .cljc whose two arms are disjoint. The
three authoring macros are its #?(:clj …) arm and are what ns-publics returns
on the JVM; the eleven runtime vars are its #?(:cljs …) arm and exist only under
ClojureScript. Both arms are inventoried — the JVM manifest generator owns the
macros, the CLJS analyzer probe owns the runtime vars — so neither host can
silently gain or lose a public.
Authoring macros¶
defview¶
- Kind: macro
- Signature:
- Description: Mints a boundary — a real React function component, and a legal
hiccup head.
argvis the ordinary one-props-map argument vector, so destructuring reads as it does in any Clojure fn.- The macro reads no body. It expands to a
defof the minted head plus a source coordinate, so a refusal raised while the body runs can name where the boundary was written. - The
fnit emits is anonymous, so nothing it binds can shadow a helper of the same name —(h/defview todo-row [p] (todo-row-body p))is safe at the ordinary spelling. - The name is also registered in re-frame's
:viewregistrar under(keyword "<ns>" "<sym>"), for forward resolution only — a tool holding a keyword the author wrote reaches the view they meant. It carries no:handler-fn, and rides thedebug-enabled?gate, so a production build registers nothing. - Hooks do not belong in a body: a body is dynamically composed, so a hook
written there would make its own call order depend on a data path. Put
hook-intensive behaviour in a React island reached through
defhost.
- The macro reads no body. It expands to a
- Example:
defhost¶
- Kind: macro
- Signature:
- Description: The interop door. Names the crossing to a foreign React
component once; the resulting var is a hiccup head anywhere, indistinguishable
from a view. Callback contracts are inferred from each prop's spelling, exactly
as at a native tag.
optscarries four optional keys —:callbacks,:slots,:server,:fallback— and any other key is refused. Anything written pastoptsis refused rather than silently dropped (:rf.error/fresco-bad-host-declaration). - Example:
event¶
- Kind: macro
- Signature:
- Description: The one callback form, for a position where the event itself is
wanted. It expands to a marked
fnand nothing else, so the value is an ordinary function. Which of two contracts it carries is selected by position, not by the name: at anon*-spelled prop a returned vector is dispatched and any other return ignored; at any other walked prop it is a render position, pure, whose return is the render output. - Example:
Reads¶
sub¶
- Kind: function
- Signature:
- Description: The ambient collector — read a subscription's value from
anywhere inside a body, including inside a
when, aforor an inlined helper. The edge is recorded where the read happens, so a branch not taken contributes no edge. The frame doors are core's rather than duplicated here:(rf/current-frame-id)and zero-arity(rf/capture-frame)are legal inside a body.
Frame boundaries¶
Two heads, one pair of opposite verbs. The frame is the tree's business, not
the root door's: a boundary in the tree is what puts a frame in context for
everything below it, and the same two heads answer for a whole-page root and for
a subtree inside one. rf/frame-root and re-frame.adapter.uix/frame-root mount
the same shared cores, so a Fresco boot line reads like a Reagent or UIx one.
frame-root¶
- Kind: Var (a legal hiccup head)
- Signature:
- Description: ENSURE a named frame for a subtree — creates it if absent,
joins it as it stands if it is already live. The opts map is the whole
rf/make-frameoption map (:idrequired, plus:doc,:fx-overrides,:url-bound?and the rest), so a frame option no longer has to detour through a hand-writtenmake-framecall before the mount. Ensuring runs at commit, which is why it is the wrong verb after SSR — seeframe-provider. A:framekey is:rf.error/frame-root-given-frame, namingframe-provider; changing a mounted boundary's:idor opts is:rf.error/frame-root-reconfiguredrather than a silent no-op.
frame-provider¶
- Kind: Var (a legal hiccup head)
- Signature:
- Description: SCOPE an existing frame to a subtree —
frame-root's sibling and its opposite verb. It creates nothing and configures nothing; it fails loud when the frame is absent (:rf.error/frame-provider-frame-absent) rather than quietly conjuring one. This is the verb afterre-frame.ssr/hydrate!and for a second root on a frame another root already ensured. An:idkey is:rf.error/frame-provider-given-id, namingframe-root.
Roots¶
Three doors and one handle — the grammar every React view adapter publishes (Spec 006 §The client root). Every one is root-scoped: a page may hold as many roots as it likes, and no call here reaches a root the caller did not name.
client-root¶
- Kind: function
- Signature:
- Description: Allocates an inert, opaque handle. No DOM work and no React
call at allocation, so it belongs under a
defonceat namespace load. One handle owns at most one React root at a time; the raw root is reachable through nothing, and liveness is read off the package's active-root set rather than off the handle. - Example:
render!¶
- Kind: function
- Signature:
- Description: The root door AND the hot-reload door. The FIRST call through a
handle creates the React root at
container; every later call updates that same root insideflushSync, so React reconciles against the tree on the page and the DOM, the subscriptions and every scrap of component state survive. Answers nil.containerandoptsare read on the first call only.optscarries root options only —:hydrate?and:identifier-prefix(React'sidentifierPrefix, a pass-through) — and REFUSES anything else::frame/:initial-eventsraise:rf.error/fresco-frame-config-misplacednaming the head that takes them, every other key:rf.error/fresco-unknown-root-option. The frame is spelled in the tree, onframe-root(ENSURE) orframe-provider(SCOPE), and the view is a whole root tree, boundary included — a later render that drops the head leaves the subtree with no frame in context, and re-rendering with the other head is a React type change that remounts everything this door exists to preserve. {:hydrate? true}: makes the FIRST call adoptcontainer's existing server-rendered DOM rather than replacing it —hydrateRoot, this root's own adoption window, and in debug builds its own recoverable-error reporter. It returns before adoption finishes, and it must be handed the same:identifier-prefixthe server render used. A MODE rather than a verb: a later call through a live handle ignores it rather than hydrating twice. Its tree SCOPEs rather than ENSUREs, and the reason is SHAPE:frame-root's ENSURE is commit-owned, so its first render emits no descendant subtree, where an adopting root must render the server's element shape on its first pass.[h/frame-provider {:frame …} …]renders its children immediately, so the shapes agree. Neither hydration step creates the frame:re-frame.ssr/hydrate!dispatches:rf/hydrateat a frame that must already exist, so a boot makes the frame first, installs the payload second and adopts the DOM third.- Example:
unmount!¶
- Kind: function
- Signature:
- Description: Takes this root down and returns the handle to inert; a later
render!through it mounts afresh. Idempotent. Leaves sibling roots' subscriptions and frames exactly where they were, and leaves the container in the document, which React empties but does not remove. Because the root sits in the package's active set,rf/destroy-adapter!releases a still-live handle's root exactly once and this door then finds nothing left to do.
Markup¶
error-boundary¶
- Kind: Var (React class component; a legal hiccup head)
- Signature:
- Description: The runtime's own error boundary, named for React's term of art. A React class, so React hands it a render-phase throw from anything below. It is not a Fresco reactive boundary: it reads no subscription, holds no cell and spends no hook.
portal¶
- Kind: Var (minted host head)
- Signature:
- Description: Hiccup into
createPortal. Three facts and nothing else: events bubble through the React tree, so an ancestor's:on-clicksees clicks inside the portalled subtree; a changed:targetis a remount, so keep it stable; and it is client-only, so the subtree is absent from a server response and:fallbackis what takes its tree position. Anchoring, dismissal and focus conduct belong to the overlay module, not here.
route-link¶
- Kind: function
- Signature:
- Description: One real anchor, as data — href and click decision taken whole from routing's late-bound seams. A plain function, not a boundary: it mints no boundary and adds no hook.
as-element¶
- Kind: function
- Signature:
- Description: The one explicit hiccup→ReactNode conversion, under the frame of
the boundary currently rendering. It exists because a declared
:renderreturn crosses unconverted: a returned hiccup vector would reach React, which refuses it. Also the answer at the two places a declaration cannot reach — a[:>]escape, and past the native fence. Where the crossing is declared, preferdefhost's:slots, which lowers those positions for every use site at once.
as-component¶
- Kind: function
- Signature:
- Description: The outward bridge — answers a real React component for a hiccup
head, so a UIx or plain-JavaScript parent mounts a minted Fresco view under the
frame it is already in. Declared once at top level, beside the view. The parent's
props arrive as the view's ordinary props map, children at
:children, and the frame comes from React context: no second root, state owner or props ABI appears anywhere.
Local state¶
reg-state¶
- Kind: function
- Signature:
- Description: The instance-key sugar. Mints one parametric subscription and one
setter event under
[:ui ::concern ikey], and nothing else.
What this door does not carry¶
The optional modules are reached separately, so an application that never asks for
one carries none of it — presence is re-frame.fresco.motion/presence, and
.forms, .overlay, .motion, .substrate and the .server SSR module each cost
a classpath entry and no bundle bytes until required. The door names none of them,
and that is the point rather than an omission: one :require here would put the
retention machine into every bundle that ever touched the door. Those modules, the
test kit and the tool tier are documented in the
Fresco API reference; they carry no
api-manifest rows of their own.
The marker keywords need no export. ::h/value, ::h/prevent, ::h/revision,
::h/checked and ::h/clear read :re-frame.fresco/…, so aliasing this namespace
as h resolves the auto-resolved spelling the guide teaches with no keyword changing
value.