Skip to content

Fresco glossary

This glossary defines Fresco-specific terms. Core re-frame2 terms such as app-db, frame, event, and subscription live in the core glossary.

Authoring

Fresco

re-frame2's native React view adapter. Fresco interprets Hiccup, reads subscriptions with h/sub, and accepts event vectors as intents. App-db, events, effects, and the event pipeline remain ordinary re-frame2.

Require it as:

[re-frame.fresco :as h]

Forms, overlays, routing helpers, the island hooks, and test tooling are separate optional namespaces.

Related: Getting started, Installation.

defview

h/defview defines a Fresco view. The view receives one props map and returns Hiccup, nil, a fragment, or a native React element at the direct-return performance level.

Use a view as a Hiccup head. Do not call it as an ordinary function:

(h/defview counter [_]
  [:main
   [:h1 "Clicked " (h/sub [:counter/count]) " times"]
   [:button {:on-click [:counter/increment]}
    "Click me"]])

[counter {}]   ;; view boundary
(counter {})   ;; raises

Related: Views and reads.

View

A function from a props map to markup, defined with h/defview. In Hiccup head position it creates an independently re-rendering boundary. A plain defn is an inline helper, not a view.

Related: Views and reads.

Boundary

An independently re-rendering unit created by h/defview. It:

  • tracks the body's h/sub reads;
  • compares props with ClojureScript =;
  • supplies the re-frame2 frame used by event intents.

Native tags, fragments, and h/defhost heads do not create Fresco view boundaries.

Related: Views and reads.

Inline helper

An ordinary function called from a view body. Its returned Hiccup is included in the caller's tree, and any h/sub calls belong to the enclosing boundary. It does not create independent re-render behaviour.

[todo-row {:key id :id id}]   ;; child boundary
(row-icon {:kind :urgent})    ;; inline helper

A plain function in Hiccup head position raises :rf.error/fresco-bad-head.

Related: Views and reads.

h/sub

The only subscription-read form inside a Fresco view. It is an ordinary function call and may appear in a let, conditional, loop, or synchronous helper.

(let [todo (h/sub [:todo/by-id id])]
  [:span (:title todo)])

A bare rf/subscribe in a view body is not an alternative. React islands use n/use-sub.

Related: Views and reads.

Read-extent law

h/sub is legal only during direct synchronous execution of the active view body, including helpers it calls immediately. A callback, promise, timer, lazy sequence, unforced delay, or other deferred computation may not carry the read outside that extent.

A read after the extent raises a structured error such as :rf.error/fresco-sub-outside-render or :rf.error/fresco-deferred-read-at-boundary. Read the value during render and close over the value instead.

Related: Views and reads.

Collector

The runtime mechanism that records the subscriptions a boundary reads during one body execution. Commit reconciles that read set. An abandoned or retried render acquires no durable subscription ownership.

Related: Views and reads.

Component ABI

The props and children contract for a Hiccup head: which values are converted, which pass by identity, where :key and :ref live, and how children arrive.

  • Fresco views receive a ClojureScript props map.
  • Declared hosts follow their callback, slot, and server contracts.
  • A React island, reached through a host, receives React props; nothing lowers Fresco event intents or controlled fields inside it.

Related: Views and reads, Interop, Islands.

Lowering

The conversion from Fresco data to React props and elements. It includes the Hiccup walk, event-intent callback creation, controlled-field behaviour, and attribute normalisation.

When diagnostics identify lowering itself as the cost owner, the local escape is returning a React element directly from the same view.

Related: Events as data, Islands.

Owned-wins merge

When a view forwards an attributes map into an element, literal keys written by the element author take precedence. Control slots such as :value, handlers, :key, and ::h/revision should not be replaceable through a generic forwarded map.

Related: Views and reads, Controlled inputs.

Read topology

The placement and grouping of subscription reads relative to a collection.

Shape Behaviour
Fine Each row reads its own entity; good for sparse updates
Coarse One view-model represents the collection; good for cheap mount or bulk replacement
Chunked One read covers a bounded block of rows
Windowed Only visible rows exist in the DOM, usually through a virtualiser

Related: Lists and collections.

Events and control

Intent

An event vector written directly at an event prop. The runtime creates a callback and dispatches the vector into the rendering view's frame.

[:button {:on-click [:todo/toggle id]}
 "Toggle"]

The Hiccup tree retains the event as ordinary data, so tests and tools can inspect it with =.

Related: Events as data.

h/event

The one marked callback form (HD-024). Expands to an ordinary function. The contract comes from the position where it is written: on* positions dispatch a returned vector; render positions must stay pure; a declared ReactNode slot refuses the mark.

[:input {:type "file"
         :on-change (h/event [e]
                      [:upload/picked
                       (js/Array.from (.. e -target -files))])}]

Captures the rendering frame when created. Use it when arguments determine the event — value-first foreign callbacks, file lists, drag data — or when the body must call browser methods such as .preventDefault.

Related: Events as data, Interop.

::h/value and ::h/checked

Reserved markers replaced at dispatch with the event target's current value or checked state. Substitution occurs only at the top level of the event vector.

::h/value is the target's .value on every control but one. A <select multiple>'s value is its selection rather than a scalar, so the marker carries a vector of the selected option values — [] when nothing is picked. Reading .value there would answer the first selected option only, which is a plausible string that quietly is not what the user chose.

[:input
 {:value    (h/sub [:draft])
  :on-input [:draft/changed ::h/value]}]

Related: Events as data, Controlled inputs.

::h/prevent

An intent wrapper that calls preventDefault and then dispatches one inner event vector. Fresco does not auto-prevent clicks; :on-submit is the one position whose data spelling prevents by default, so a submit intent needs no wrapper. A callback always owns its own event and is never auto-prevented.

[:a.nav-link
 {:href      "#"
  :on-click  [::h/prevent [:todo/filter-active]]}
 "Active"]

Related: Events as data.

Controlled field

An input whose displayed value comes from app-db and whose user edits return as event intents. Fresco's controlled path provides:

  • synchronous same-turn convergence;
  • committed-value echo;
  • caret and selection preservation;
  • IME composition safety;
  • explicit reset through ::h/revision.

A React island does not provide this repair. Keep controlled text fields on the interpreted Fresco path.

Related: Controlled inputs.

::h/revision

A reserved prop for controlled text. Change it when the field should re-baseline to the current model value after a reset, rejection, rewrite, or server normalisation.

[:input
 {:value       (h/sub [:field/value id])
  ::h/revision (h/sub [:field/revision id])
  :on-input    [:field/edit id ::h/value]}]

Reset is not inferred from value equality. The exact namespaced keyword is required; bare :revision is an ordinary attribute.

Related: Controlled inputs.

Buffered field

forms/buffered-field is an optional forms component that places an app-db draft in front of a controlled model value. It supports commit, cancel, rejection, rewrite, and revision-based reset.

Related: Forms.

Keyboard map

A map from DOM .key strings to event intents, used at :on-key-down or :on-key-up.

{:on-key-down
 {"Enter"  [:editor/commit]
  "Escape" [:editor/cancel]}}

Unlisted keys are ignored. There is no modifier DSL; use h/event for cases such as Ctrl+Enter. Key maps suppress matches during IME composition.

Related: Events as data.

Interop

defhost

h/defhost declares a foreign React component once. Callback contracts are inferred from each prop's spelling, as on a native tag; the declaration can also define:

  • a :callbacks override, :event or :render, for an on*-named render prop;
  • ReactNode slots;
  • a server policy;
  • a Client-only fallback.
(h/defhost date-picker DatePicker
  {:slots  #{:calendar}
   :server :client-only})

Keep the JavaScript require in a .cljs host namespace.

Related: Interop.

ReactNode slot

A host prop declared to contain React content, such as a modal title, footer, or Suspense fallback. Hiccup supplied to the slot is converted to React elements under the captured frame. Undeclared props receive Hiccup vectors as ordinary data.

Related: Interop.

as-element

h/as-element explicitly converts Hiccup to a React element for a render prop, foreign callback, or other ReactNode position.

{:render-item
 (fn [row]
   (h/as-element
    [row-view {:id (:id row)}]))}

Related: Interop, Lists and collections.

as-component / outward bridge

h/as-component turns a Fresco view into a real React component that a native React, UIx, or JavaScript parent can mount under the existing frame provider. It does not create another root or state owner.

Related: Interop.

Portal

h/portal renders Hiccup into another DOM container through React createPortal while preserving frame and context. React events bubble through the React tree rather than the DOM placement.

Use the overlay module instead when the UI should live on the browser's native top layer.

Related: Interop, Overlays and focus.

Server policy

The SSR contract for a host or native component:

  • Render: execute on the server and produce deterministic React HTML;
  • Client-only: do not execute on the server; produce a deterministic fallback or nothing until the browser adopts the root.

Foreign hosts and named native components default to Client-only. Native Hiccup and intrinsic React elements render by default.

Related: SSR and hydration, Interop.

Raw escape (:>)

[:> Component props ...] mounts a foreign React component without a lasting host declaration. It is useful for migration or a true one-off. Repeated crossings should use h/defhost so callback contracts, slots, and server policy remain explicit.

Related: Interop.

Islands

re-frame.fresco.native

The optional hooks namespace, usually aliased n. It holds exactly two public names, n/use-sub and n/use-frame, which are how a React island reaches Fresco state. It carries no element grammar and no component macro: an island is written in raw React or UIx.

[...] always means interpreted Hiccup. A React element is never interpreted; it passes through unchanged.

Related: Islands.

n/use-sub

A React hook that subscribes to a re-frame2 query from inside a React island. It reads through the same cell table as h/sub, so the read joins the same membership and Xray rosters, and it obeys React's rules of hooks: call it unconditionally at the top level of the component.

Related: Islands.

n/use-frame

A React hook returning frame-locked operations — :dispatch, :dispatch-sync, and :subscribe — for the frame the island is mounted in, pinned to that frame's incarnation.

Related: Islands.

Island

A React component, raw React or UIx, mounted through h/defhost under the same React root and re-frame2 frame as the surrounding Fresco application. It is appropriate for hooks, vendor widgets, and high-rate host-private mechanics.

Xray names and times the crossing, while the inner React tree remains host-opaque.

Related: Islands.

Performance ladder

Five explicit implementation levels:

  1. ordinary Fresco;
  2. tuned read topology;
  3. a React element returned directly from an existing view;
  4. a React island;
  5. a native screen.

Related: Performance, Islands.

Escape-benefit rule

Keep a native escape only when it:

  • recovers at least 20% of the measured interaction;
  • saves at least 2 ms at p95; or
  • converts a failed user-visible budget into a pass.

Otherwise remove it.

Related: Performance.

State homes

One state owner

Application-visible state lives in re-frame2 app-db. Fresco does not add a component-local reactive store. A host may retain private mechanics only when they are not a hidden duplicate of an application fact.

Related: Ephemeral state.

motion/presence

Optional exit-retention head from re-frame.fresco.motion. Keeps keyed children for :timeout-ms after their data leaves app-db so CSS exit transitions can run. Merges each child's ::motion/mounting / ::motion/unmounting override map into it while it is in that phase — an element's attributes or a view's props. Not an animation system.

Related: Motion and presence, Ephemeral state.

Pressure valve

A legitimate home for UI state under the one-state-owner rule:

  • an explicit app-db address;
  • the forms module for drafts and form control;
  • native host state for high-rate private mechanics;
  • browser-owned state as an explicit interop choice;
  • presence retention for pixels that outlive removed data.

Related: Ephemeral state.

Overlay

re-frame.fresco.overlay popover and modal primitives. They use the browser's native top layer. App-db owns :open?; :on-dismiss is an event; the browser owns stacking, light-dismiss, modal focus trapping, and focus restoration.

A closed overlay has no DOM node, listener, or active body subscriptions.

Related: Overlays and focus.

A routing helper on the door, called as h/route-link, that returns a real anchor and encodes navigation as a Fresco intent. It supports route ids and params, native link semantics, and link-local veto behaviour. :prefetch :intent warms the destination on hover, focus and touch, filling those three positions with routing's own prefetch event; supply a value at one of them yourself and the render is refused, because one position carries one intent.

It is an inline function, not a separate view. Active-state styling comes from a route subscription comparison.

Related: Routing and navigation.

View-scoped read

A resource whose lifetime is a local view rather than the current route. It has no dedicated mechanism: the event that decides the data is wanted ensures it under an owner, and the event that dismisses the view releases that owner.

(rf/reg-event :suggestions/wanted
  (fn [_ [_ q]]
    {:fx [[:dispatch [:rf.resource/ensure
                      {:resource :app/suggestions
                       :params   {:q q}
                       :owner    [:suggestions]
                       :cause    [:suggestions/wanted q]}]]]}))

Resource subscriptions are passive in every case — they project the cache and never fetch. An owner pins its entry against GC until it is released.

Related: Async resources, Resources glossary.

Testing

Test kit

Two namespaces:

  • re-frame.fresco.test, usually ht, for pure and semantic tests;
  • re-frame.fresco.test.mounted, usually hm, for mounted React and DOM tests.

Related: Testing.

Testing ladder

Level Proves Mechanism
L0 Handlers, subscriptions, transitions Pure function calls
L1 Intents, codecs, revision laws, macro expansion Data and property tests
L2 One hook-free body as a semantic tree ht/tree
L3 React lifecycle, hooks, hosts, error boundaries Mounted facade
L4 IME, caret, focus, hydration, performance Real browser engines

A lower level does not prove the equality of a higher level.

Related: Testing.

Semantic harness

ht/tree runs one hook-free Fresco view body with injected subscription fixtures and returns a semantic tree. Nested views remain represented as calls. Hooks, hosts, and raw React elements are refused and belong at L3.

Related: Testing.

Mounted facade

The hm namespace for L3 tests. It provides isolated-frame mount and hydrate, rerender, dispatch-and-settle, settle, virtual-clock advancement, unmount, and assert-clean! residue checking.

Related: Testing.

Sabotage control

A deliberately broken twin of an important test or measurement. It proves that the instrument moves when the input is wrong and prevents an empty population from passing vacuously.

Related: Testing.

Canonical DOM

A normalised DOM serialisation used for differential comparison. Attribute names are ordered so equivalent DOM does not differ only because properties were inserted in a different sequence.

Canonical DOM is distinct from semantic-tree equality, exact server bytes, and hydrated browser behaviour.

Related: Testing, Migrating from Reagent.

Diagnostics

Causal lens

The diagnostic sequence used by Xray:

event
  → subscriptions recomputed
  → values changed
  → views notified
  → bodies run
  → React commit
  → browser paint

Render, commit, and paint are separate claims.

Related: Diagnostics.

Explain-render

Xray's answer to “why did this view run?” It reports the cause category, changed reads or props, current read set, fan-out, completeness, and evidence loss.

Related: Diagnostics.

Hot-view advisor

A diagnostic ranking that combines time, frequency, read churn, and fan-out, then classifies the pressure as computation, topology, lowering, React, or layout. It recommends the smallest credible remedy and never auto-promotes code to native.

Related: Diagnostics, Performance.

Loss labels

Explicit labels for incomplete evidence:

  • :unknown;
  • :opaque / :no-static-analysis;
  • :host-opaque;
  • :cap;
  • :uncorrelated.

Missing evidence is not represented as an empty result.

Related: Diagnostics.

Complaint catalogue

The stable :rf.error/* and :rf.warning/* identifier set, including cause, recovery, and source links where available. Tests assert the id, not the human message.

Related: Diagnostics.

Production erasure

Removal of development diagnostics, evidence machinery, source locations, and complaint messages from default release bundles. Optional performance timing has a separate compile-time flag and is disabled by default.

Related: Diagnostics.

Lifecycle and delivery

client-root, render!, and unmount!

The Fresco root lifecycle — the same three names every React view adapter publishes (Spec 006 §The client root).

h/client-root allocates an inert, opaque handle. No DOM work, no React call, so it belongs under a defonce at namespace load.

h/render! is the root door and the hot-reload door in one verb. Its FIRST call through a handle creates the React root at the node it is given; every later call updates that same root, so the DOM, the subscriptions and every scrap of component state survive. Its opts carry React-root options only — :hydrate? and :identifier-prefix, both read on the first call. The frame is named in the tree, by [h/frame-root {:id …}] (ENSURE) or [h/frame-provider {:frame …}] (SCOPE), and rides every render rather than only the first. Initial events run in order before first paint.

h/unmount! tears the root down and is safe to call more than once; a later h/render! through the handle mounts afresh. It destroys no frame: a frame outlives the boundary that ensured it.

(defonce app-root (h/client-root))

(defn ^:dev/after-load mount! []
  (h/render! app-root
             [h/frame-root {:id :rf/default :initial-events [[:app/init]]}
              [app-shell {}]]
             (js/document.getElementById "app")))

Related: Installation.

{:hydrate? true}

Two calls complete hydration, and neither creates the frame:

  • re-frame.ssr/hydrate! installs the server payload into a client frame that must already exist (rf/make-frame made it);
  • h/render! with {:hydrate? true} on its FIRST call through a handle adopts existing server DOM for one Fresco root, under an [h/frame-provider {:frame …}] that SCOPEs the frame the payload landed in. It is a first-call mode, not a verb: a later call through a live handle updates the root it already owns and ignores the key.

State hydration must run before DOM adoption. frame-provider catches the boot that never made the frame at all — it refuses an ABSENT frame rather than scoping a subtree to nothing. It does not detect a frame that is live but never hydrated: liveness is the whole of the check.

Related: SSR and hydration.

Error boundary

h/error-boundary is a React error region with :fallback, :reset-key, and :on-error. It is different from a re-render boundary; only the error boundary catches descendant render and lifecycle exceptions.

Expected failures remain ordinary app-db state.

Related: Errors.

User-visible budget

A performance requirement expressed as an observable user outcome, such as:

  • discrete interaction paint within 50 ms p95;
  • controlled echo within one frame;
  • broad operation within 100 ms p95;
  • zero teardown residue.

Synthetic benchmark scores do not replace these budgets.

Related: Performance.

Shadow comparison

A migration witness that mounts a reference implementation and candidate under isolated equivalent state, drives both with one script, and compares canonical DOM plus event-intent streams at each checkpoint.

Related: Migrating from Reagent.