re-frame.adapter.reagent¶
re-frame.adapter.reagent binds re-frame2's substrate-agnostic core to Reagent, the browser-default reactive substrate. Requiring it gives you four things:
- the
adapterspec you pass to(rf/init! …)at boot; - the client root —
client-root,render!,unmount!— the one React Root your page mounts through; flush-views!, the test helper;set-hiccup-emitter!, the SSR render-to-string seam.
There is no per-substrate hook surface here. The substrate-agnostic ergonomic surface (capture-frame, with-frame, with-new-frame, frame-provider) and the reg-view registry live in re-frame.core. They compose across every substrate. The dependency is one-way: this adapter depends on re-frame.core; core never depends on it.
The adapter ships in two artefacts: day8/re-frame2-reagent (full) and day8/reagent-slim (slim). See the variant table under adapter. For substrate choice, see Use UIx or reagent-slim.
The adapter spec¶
adapter¶
- Kind: Var (map)
- Signature:
- Description: The Reagent adapter map: the substrate spec you pass to
(rf/init! ...)to install the browser-default Reagent substrate (stockreagent.core/reagent.dom.client).- There is no default-adapter registry and no keyword form. Require the adapter ns and pass its
adapterVar explicitly at the call site. - When this adapter is installed,
current-adapter(inre-frame.core) returns this map itself — that is the installed-adapter value, and its presence is how you ask whether an adapter is seated. The discriminator is a KEY on it:(:kind (rf/current-adapter))reads:rf.adapter/reagent. - The Reagent
frame-provideris the substrate-agnostic provider fromre-frame.core; children stay trailing-positional hiccup ([rf/frame-provider {:frame …} & children]).
- There is no default-adapter registry and no keyword form. Require the adapter ns and pass its
- Example:
Reagent ships in two variants, full and slim. Both publish their adapter at the same canonical ns, re-frame.adapter.reagent, so the require and the init! line are identical. You select the variant by the Maven coordinate you depend on, not by the boot line:
| Variant | Maven coordinate | Adapter ns (require) | Includes | Use when |
|---|---|---|---|---|
| Full | day8/re-frame2-reagent |
re-frame.adapter.reagent |
stock Reagent (reagent.core, reagent.dom.client, reagent.dom.server) |
client apps that may also render to a string on the JVM |
| Slim | day8/reagent-slim |
re-frame.adapter.reagent (renamed from the in-tree -slim ns at publication) |
the reagent2 rewrite; static HTML export via a pure-CLJS reagent2.dom.server, no react-dom/server |
browser-only bundles; ~7–10 KB gzipped smaller (up to ~22–27 KB where the HTML-export path was in play) |
A build depends on exactly one variant, so the adapter ns is single-source per app. You select slim vs full through your deps.edn coordinate.
In-repo :git/sha consumers require re-frame.adapter.reagent-slim directly, because the monorepo carries both adapters on one classpath. The publication step renames it to re-frame.adapter.reagent before packaging the jar.
The slim variant is bundle-isolated: a dedicated isolation gate verifies that stock Reagent / react-dom/server don't leak into builds that select it.
The migration (a four-line swap) is in Use UIx or reagent-slim.
The client root¶
A browser app needs one React Root for the life of the page: created once, re-rendered on every hot reload, released on teardown. These three functions own that Root so your entry namespace does not have to. Allocate the handle under a defonce, render through it from the ^:dev/after-load hook, and never touch reagent.dom.client yourself. The whole recipe is in Boot and mount an app.
(defonce app-root (reagent-adapter/client-root))
(defn ^:dev/after-load mount! []
(when-let [el (and (exists? js/document)
(js/document.getElementById "app"))]
(reagent-adapter/render! app-root
[rf/frame-root {:id :rf/default :initial-events [[:app/initialise]]}
[app-view]]
el)))
The Root these functions manage is tracked by the same active-root ownership as the adapter's one-shot render slot, so rf/destroy-adapter! releases it too — exactly once. The raw React Root is never exposed.
client-root¶
- Kind: function
- Signature:
- Description: Allocate an inert client-root handle and return it. Does no DOM work, so it is safe at namespace load under a
defonce, in tests, and on Node. The React Root is created (or hydrated) by the firstrender!through the handle.- The handle is opaque: hold it, hand it to
render!andunmount!, and nothing else.
- The handle is opaque: hold it, hand it to
- Example:
render!¶
- Kind: function
- Signature:
- Description: Render
render-tree(hiccup) through the client-roothandleat the DOM elementmount-point. Returns nil.- The first call creates the React Root at
mount-pointand renders into it. With{:hydrate? true}it hydrates the server-rendered markup already insidemount-pointinstead (once; seere-frame.ssr). - Every later call updates that same Root with the new tree: no second
create-root, no second hydration. That is what makes one call both the boot path and the^:dev/after-loadhook.mount-pointis read on the first call only. - After
unmount!, or afterrf/destroy-adapter!has released the Root, the nextrender!mounts afresh.
- The first call creates the React Root at
- Example:
unmount!¶
- Kind: function
- Signature:
- Description: Unmount the React Root
handleholds and return the handle to inert. Returns nil.- Idempotent: a second call, or a call after
rf/destroy-adapter!has already released the Root, does nothing.
- Idempotent: a second call, or a call after
- Example:
Test helpers¶
flush-views!¶
- Kind: function
- Signature:
- Description: Wraps React's
act()for tests. Flushes pending Reagent renders synchronously and returns nil.- 0-arity: drains the queued renders and effects.
- 1-arity: runs the thunk
f, then the synchronous render drain, insideact(). - When
act()is unreachable in the current React build, this degrades to a plain synchronous flush.fstill runs and the render queue still drains, just without theact()wrapper. - Surfaced identically across all substrates: same name, same adapter-ns location, same nil-return.
- Example:
Server-side rendering¶
set-hiccup-emitter!¶
- Kind: function
- Signature:
- Description: Install a render-tree → HTML fn: the hiccup → HTML emitter used by render-to-string.
- Last call wins; pass
nilto reset. - Normally you don't call this directly. Requiring
re-frame.ssrresolves the late-bind hook and wires the emitter for you. - It is the Reagent-side late-bind seam for SSR, matching the parallel seam on the UIx adapter.
- Last call wins; pass
- Example:
See also¶
re-frame.core— the substrate-agnostic surface (capture-frame,with-frame,with-new-frame,frame-provider,reg-view) and the lifecycle surface (init!,destroy-adapter!,current-adapter).- Boot and mount an app — the entry-namespace recipe built on the client root.
re-frame.adapter.uix— the hooks-first React substrate and its parallel adapter surface.re-frame.ssr— server-side rendering; wiresset-hiccup-emitter!for you.- Use UIx or reagent-slim — the substrate-choice how-to, including the slim swap.
- Views — why the substrate only shows up in the view body.
- Adapter and substrate — the seam, and the thing it binds to, defined.