Skip to content

The re-frame Clojars page contains dependency coordinates for Maven/deps/Lein.

Unreleased

Nothing yet.

1.4.7 (2026-04-30)

A small follow-up to 1.4.6 in response to user feedback against the new re-frame.macros namespace introduced there. Two themes: complete the mirror so it's actually a drop-in for re-frame.core, and fold in a parallel mirror for re-frame.alpha. The re-frame.macros namespace itself has been renamed to better describe what it is (see Changed below).

Changed

  • Namespace rename: re-frame.macrosre-frame.core-instrumented. The instrumented mirror of re-frame.core introduced in 1.4.6 has been renamed to better describe what it is rather than how it's implemented. The re-frame.macros namespace is gone — change your :require from [re-frame.macros :as rf] to [re-frame.core-instrumented :as rf]. Behaviour is unchanged.

Added

  • re-frame.core-instrumented is now a complete drop-in for re-frame.core. The 1.4.6 cut covered 8 source-meta-capturing surfaces (dispatch, dispatch-sync, subscribe, reg-event-db/-fx/-ctx, reg-sub, reg-fx); user feedback flagged that reg-sub-raw was missing, and that the partial coverage broke the alias-swap pattern for any real-world app that also touched clear-event, inject-cofx, path, console, etc. This release brings the namespace to parity:
  • New source-meta-capturing macros: reg-sub-raw, reg-cofx, reg-event-error-handler, dispatch-with, dispatch-sync-with, dispatch-and-settle. Each decorates the registered handler / dispatched vector with {:file :line} under the appropriate registrar kind. CLJS production builds (goog.DEBUG=false) DCE the meta-attach branch the same way the existing macros do.
  • Pure passthroughs (def re-exports): every other public re-frame.core symbol is re-exported as a def so the alias swap covers the full surface — clear-event, clear-sub, clear-fx, clear-cofx, clear-subscription-cache!, clear-global-interceptor, inject-cofx, path, enrich, after, on-changes, ->interceptor, debug, unwrap, trim-v, reg-global-interceptor, set-loggers!, console, tag-schema, validate-trace?, set-validate-trace!, register-trace-cb, remove-trace-cb, register-epoch-cb, remove-epoch-cb, assemble-epochs, version, query-v-for-reaction, live-query-vs, get-coeffect, assoc-coeffect, get-effect, assoc-effect, enqueue, make-restore-fn, purge-event-queue, add-post-event-callback, remove-post-event-callback, plus the legacy register-handler / register-sub aliases.

  • re-frame.alpha-instrumented — the same instrumentation pattern, mirroring re-frame.alpha. Source-meta capturing macros for the dispatch/registration surface alpha re-exports from core (dispatch, subscribe, reg-event-db, reg-sub, etc.); pure passthroughs for the rest of alpha's surface so the swap from [re-frame.alpha :as rf] to [re-frame.alpha-instrumented :as rf] is alias-only. Alpha-specific surfaces (reg, sub, reg-flow) pass through as functions for now — instrumenting them is deferred until alpha stabilises.

1.4.6 (2026-04-29)

This release is focused on making re-frame more amenable to pair programming with an AI like Claude Code or Codex.

Specifically, the changes included in this release are focused on supporting re-frame-pair, a SKILL which teaches an AI how to interact with, and introspect, a running re-frame application.

Along the way, these improvements help other tooling, like re-frame-10x, which is human oriented.

The focus has been on new namespaces like re-frame.tooling and re-frame.macros, and on tracing code execution within a running application.

See the Debugging & Instrumentation guide and the re-frame.tooling page for the longer-form story.

Added

Discoverability
  • re-frame.tooling — discoverability namespace for tooling consumers. Re-exports the supported surface (dispatch overrides, trace cb / schema, live subscription cache, registrar atom) without renaming or moving any symbol — source-of-truth definitions stay in their original namespaces. Require ONE namespace to find the contract:
    (require '[re-frame.tooling :as rft])
    (rft/dispatch-with [:my-event] {:effect-id (fn [_] ...)})
    (rft/register-trace-cb :my-tool (fn [traces] ...))
    @rft/query->reaction               ;; live subscription cache
    @rft/kind->id->handler             ;; registrar atom
    
Source-meta opt-in (re-frame.macros)
  • re-frame.macros — drop-in macro replacements/mirror for key re-frame.core functions. These macros capture call-site {:file :line} information to facilitate tracing and, as a result, tooling like re-frame-10x and re-frame-pair can accurately understand how the dynamics of a running application relate to source code:
    • functions mirrored — re-frame.macros/dispatch, dispatch-sync, subscribe, reg-event-db, reg-event-fx, reg-event-ctx, reg-sub, reg-fx
  • to use this new feature, swap re-frame.core for re-frame.macros in your :require:

    ;; function API — no source-meta
    (:require [re-frame.core   :as rf])
    
    ;; macro API — same call shape, source-meta captured
    (:require [re-frame.macros :as rf])
    
  • In CLJS production builds (goog.DEBUG=false) the meta-attach branch is eliminated by Closure dead-code elimination, so the macro reduces to a bare re-frame.core/... call with zero runtime overhead.

  • Going forward you should prefer to use these macros instead of their function counterparts, UNLESS you need them in a value position, for example (apply reg-sub ...), (map reg-event-db ...), (partial reg-fx ...).
Dispatching events
  • diagnostic versions of dispatch, useful in testing and experimentation.
  • re-frame.core/dispatch-with, dispatch-sync-with — fire event with selected fx handlers temporarily mocked/substituted for the duration of the dispatch and any synchronous :fx [:dispatch ...] cascade. Stubs ride on event metadata, not global state, so no try/finally is needed and overlapping calls don't interfere. Useful for dry-run dispatches in dev (observe :effects / app-db change while stubbing :http-xhrio / navigation), dispatch-scoped test stubs, and reversible REPL exploration.

    (re-frame.core/dispatch-with
      [:user/login {:email "..." :password "..."}]
      {:http-xhrio (fn [req]
                     (re-frame.core/dispatch
                       (conj (:on-success req) {:stubbed true})))})
    
  • re-frame.core/dispatch-and-settle — allows you to fire event and return a deferred value that resolves once the event AND its synchronous :fx [:dispatch ...] cascade have settled. Resolves to {:ok? true :root-epoch <id> :cascaded-epochs [<id> ...]} on success or {:ok? false :reason :timeout :event ev :captured-epochs [...]} on timeout. CLJS returns a JS Promise; CLJ returns a clojure.core/promise. Accepts an opts map: :timeout-ms (default 5000), :settle-window-ms (default 100), :include-cascaded? (default true), and :overrides — an fx-id → stub-fn map composed with dispatch-with's contract and propagated through the cascade. Useful for tests and diagnostics that need to wait for a cascade to finish before asserting on the resulting state.

    ;; CLJS — await the promise
    (-> (re-frame.core/dispatch-and-settle [:cart/checkout])
        (.then (fn [result] ...)))
    
    ;; Override selected fx while awaiting the cascade
    @(re-frame.core/dispatch-and-settle
       [:cart/checkout]
       {:overrides {:http-xhrio stub-success}})
    
Subscription accessors
  • re-frame.core/live-query-vs, re-frame.core/query-v-for-reaction — public, cache-shape-stable accessors for tool consumers that previously had to fake them by walking internal query->reaction keys.
  • live-query-vs returns a snapshot sequence of every currently- live cached query vector. Useful as a sub-cache-size probe: (count (re-frame.core/live-query-vs)).
  • query-v-for-reaction is the inverse of subscribe: given a reaction held by tooling, return the query-v that produced it (or nil if unknown / disposed). Backed by an object-identity- keyed reverse map maintained alongside the subscription cache; entries clear when the reaction is disposed. The reverse-map insert is gated on is-trace-enabled? so it costs nothing in production builds without tracing.
Tracing
  • re-frame produces detailed trace data describing how an event is processed and how the subsequent reactive UI updates occur.
  • re-frame.core/tag-schema — describes the schema for this trace (for use by tooling).

    • The schema describes :tags for every op-type re-frame emits (:event, :event/handler, :event/do-fx, :sub/create, :sub/run, :sub/dispose, :render, :raf, :raf-end, :reagent/quiescent, :sync). Each entry lists :required keys, :optional keys, and a one-line :doc. Doc-only by default; downstream tooling reads it as a load-bearing contract for the trace stream. Adding a key is additive; renaming or removing a key is breaking and must go through a deprecation cycle.
  • re-frame.core/validate-trace?, set-validate-trace! — opt-in runtime trace tag validation. With validation on, finish-trace checks :tags against tag-schema and warns via console :warn on missing required keys or unknown keys. Off by default; intended for dev / CI to catch schema drift in re-frame core and in third-party trace emitters early.

    (re-frame.core/set-validate-trace! true)
    
  • re-frame.core/register-epoch-cb, remove-epoch-cb, assemble-epochs — assembled-epoch callback alongside register-trace-cb. Where register-trace-cb delivers the raw trace stream, register-epoch-cb delivers one assembled record per :event trace, partitioning each batch into the dispatch / handler / do-fx / sub-create / sub-run shape downstream tools want. Each epoch carries its own :dispatch-id and :parent-dispatch-id; consumers wanting a tree-shaped view of a user-fired event plus its chained children build it post-delivery by walking parent-id pointers. Same gating as register-trace-cb (opt-in via :closure-defines re_frame.trace.trace_enabled_QMARK_ true) and same delivery boundary (debounced batches on CLJS, synchronous on CLJ at the outermost trace's finish). The cascade- settled signal that some consumers want lives separately under dispatch-and-settle — keeping the two decoupled lets register-epoch-cb ship without depending on async-settle infrastructure.

Diagnostics
  • re-frame.core/version — runtime-readable string identifying the deployed re-frame artifact. Useful for tooling, instrumentation, and version-floor probes (e.g. re-frame-pair's read-version-of) that need to know which re-frame they're running against without parsing pom.xml at runtime. Re-exported from re-frame.config/version; mirrored as re-frame.alpha/version.

  • re-frame.config — new namespace exposing version. Under CLJS this is a goog-define; apps embedding re-frame can override at build time via shadow-cljs :closure-defines to bake the resolved git-tag version into release artifacts:

    :closure-defines {re-frame.config/version :day8.dev/git-app-version}
    

Modeled on re-com.config/version so version-probe code can read both libraries with the same global-path lookup (goog.global.<lib>.config.version).

Changed

  • re-frame.registrar/register-handler now logs a console warning on duplicate handler registrations in production builds (goog.DEBUG=false) or before re-frame's hot-reload loaded? gate is set. Hot-reload from figwheel / shadow-cljs after page load remains silent (the same gate already used elsewhere). In production a duplicate reg-event-db / reg-sub / reg-fx for the same id almost always indicates a bug — typo, copy-paste, ns load order — so the warning surfaces it loudly. Apps relying on silent overwrite (rare, almost certainly bug-shaped) should investigate.

1.4.5 (2026-04-12)

Fixed

  • Unreachable code warning during advanced compilation in validate-inputs (#827)
  • Broken documentation link in cofx-as-fx warning message (#834)

Changed

  • Upgraded shadow-cljs in examples to 3.2.0 for ClojureScript 1.12.42 compatibility (#833)
  • Removed alpha toggle demo from todomvc example
  • Pinned GitHub Actions to commit SHAs for supply chain security
  • Migrated docs deployment to Pages artifact

1.4.4 (2026-01-03)

Added

  • :forever and :no-cache Subscription (alpha) lifecycles. See: https://day8.github.io/re-frame/FAQs/alpha/#benefit-more-lifecycles
  • Flows (alpha): added tracing. See day8/re-frame-10x v1.11.0 for an implementation of tracing in the "flows" tab.

Fixed

  • Flows (alpha) now run in response to (non-alpha) re-frame.core events

  • Subscription (alpha) signal & calculation functions always receive a map as the query, even when invoked with a vector query, or invoked with re-frame.core/subscribe. This is meant to provide stability, so users can write reg :sub handlers wtihout needing to think about whether the callsite is alpha, legacy, map or vector.

Changed

  • Subscriptions (alpha) are more careful to ensure the memory-safety of their input signals. See: https://day8.github.io/re-frame/FAQs/alpha/#benefit-safe-subscriptions

  • Changed flows to calculate the new app-db earlier in the chain.

  • Benefit: user-defined interceptors can access post-flow app-db value.
  • Drawback: they can't access the value of app-db directly resulting from the event handler.
  • Drawback: they can't access flow-related effects like :reg-flow.

  • Added a :re-frame/pre-flow-db key to the context

  • This way, user-defined interceptors can still access the app-db value resulting directly from the event handler.