06 — Routing¶
Routes in re-frame2 are data. You register a route with metadata — :path, :params, :query, :on-match, :on-error, :can-leave — and the runtime turns URL changes into events the same way every other input source does. There's no parallel router state; current route lives in app-db under :rf/route; navigation is just dispatching an event; in-flight navigation is just an event sequence the cascade is mid-way through.
The point isn't novelty — every SPA framework has a router. The point is that routing-as-state means the router is debuggable with the same tools that debug everything else. Time-travel works. The trace bus sees navigation. Tests dispatch :rf.route/navigate like any other event. There's no special "router debug mode" because the router doesn't have its own mode.
This chapter covers the registration shape, the dispatch / sub / fx surface, and the helpers that map URLs to/from route ids. For nav-token semantics, :can-leave flows, query strings, and multi-frame routing, see Guide ch.19 — Routing reference. The normative source is 012-Routing.md.
Registration¶
reg-route¶
- Kind: macro
- Signature:
- Description: Register a route as data. The id is a keyword you'll later dispatch against (
[:rf.route/navigate :route/cart]); the metadata carries the URL shape, the match events, and the guards. - In the wild: routing · realworld
A minimal route¶
:path is the URL shape — colon-prefixed segments capture into :params. :on-match is the event vector (or vector of event vectors) the runtime dispatches when the route activates. That's the whole minimal contract; everything else is optional.
Reserved metadata keys¶
| Key | Notes |
|---|---|
:doc |
Free-form description; pair tools read this. |
:path |
The URL shape — /users/:id/posts/:slug, etc. |
:params |
Schemas for path segments (per Spec-Schemas). |
:query |
Schemas for query-string keys. |
:query-defaults |
Default values for query keys absent from the URL. |
:query-retain |
Keys to preserve across navigations to other routes. |
:tags |
Free-form classification — #{:auth-required :admin-only :public}. |
:parent |
Another route id; builds a chain readable via :rf.route/chain. |
:on-match |
Event vector(s) to dispatch when the route activates. |
:on-error |
Event vector dispatched if any :on-match event errors. |
:can-leave |
Predicate or guard event; blocks navigation when truthy. See Guide ch.19 — Navigation blocking. |
:scroll |
Scroll-restoration behaviour for this route. |
Canonical detail in 012-Routing.md; the metadata schema is Spec-Schemas §:rf/route-metadata.
URL helpers¶
match-url¶
- Kind: function
- Signature:
- Description: "What route does this URL match?" Pure — JVM-runnable; useful for server-side rendering and tests.
route-url¶
- Kind: function
- Signature:
- Description: "Render this route to a URL." The inverse of
match-url. Pure; JVM-runnable.
route-link¶
- Kind: registered view (function)
- Signature:
- Description: A registered view at
:route/link. Renders an<a>with the righthrefand intercepts plain primary-button clicks to dispatch:rf/url-requestedinstead of navigating natively. - Example:
- In the wild: routing · realworld
route-link click semantics¶
A plain primary-button click (no modifier keys, no defaultPrevented) calls .preventDefault and dispatches:
Modifier-key clicks (cmd / ctrl / shift / alt) and middle-button clicks defer to the browser so the native href opens in a new tab. A caller-supplied :on-click runs first; if it calls .preventDefault (or otherwise leaves defaultPrevented true) the framework's interception is skipped. Keys other than :to / :params / :query / :fragment / :on-click pass through to the underlying <a> element.
Detailed semantics in 012-Routing.md §Linking from views.
Events¶
These are the standard events the runtime dispatches (or you dispatch) around routing.
| Event | Notes | Spec |
|---|---|---|
:rf.route/navigate |
Navigate to a registered route. Args: {:to :route-id :params {...} :query {...}}. |
012 |
:rf.route/handle-url-change |
Default handler for :rf.route/transitioned. You'd override this if you want custom transition handling. |
012 |
:rf.route/transitioned |
The browser URL changed (popstate, pushState, etc.). The runtime dispatches this; you read it. | 012 |
:rf/url-requested |
The user clicked a framework-owned link. route-link synthesises this event; you usually let the default handler take it. |
012 |
:rf.route/navigation-blocked |
A :can-leave guard rejected a navigation. The pending nav slot in app-db carries the rejected navigation. |
012 |
:rf.route/continue |
User-dispatched event proceeding a blocked navigation — "yes, leave the page." | 012 |
:rf.route/cancel |
User-dispatched event abandoning a blocked navigation — "stay here, drop the pending nav." | 012 |
Subscriptions¶
The full :rf/route slice is {:id :params :query :transition :error}. The standard subs are projections of that slice plus a couple of conveniences.
| Sub | Returns | Spec |
|---|---|---|
:rf/route |
The full :rf/route slice {:id :params :query :transition :error} |
012 |
:rf.route/id |
Current route id | 012 |
:rf.route/params |
Current path params | 012 |
:rf.route/query |
Current query params | 012 |
:rf.route/transition |
:idle / :loading / :error |
012 |
:rf.route/error |
Current error map (when :transition = :error) |
012 |
:rf.route/fragment |
Current URL fragment (string or nil) |
012 |
:rf.route/chain |
Vector of route ids from parent-most to current (per :parent links) |
012 |
:rf/pending-navigation |
The pending-nav slot (per :rf/pending-navigation schema) when a navigation is blocked; nil otherwise |
012 |
Fx¶
| Fx | Args | Platforms | Notes |
|---|---|---|---|
[:rf.nav/push-url url-string] |
URL string | :client |
Push a new URL onto the browser history. |
[:rf.nav/replace-url url-string] |
URL string | :client |
Replace the current URL without adding a history entry. |
[:rf.nav/scroll scroll-spec] |
scroll-spec map | :client |
Restore or set scroll position. |
[:rf.route/with-nav-token {:do <fx-entry> :nav-token <token>}] |
universal | universal | Wrap an fx with a navigation token. If the token has been superseded by a later navigation, the wrapped fx is suppressed and :rf.route.nav-token/stale-suppressed fires. |
The nav-token wrapper is what makes "user navigates away mid-load" safe: the older load's reply carries the stale token, the runtime suppresses it, and you don't see the older page's data overwrite the newer page's state. Full semantics in Guide ch.19 — Navigation tokens.
See also¶
- 01 — Core —
reg-routerowed in registration. - 09 — SSR — routes participate in SSR; the active route's
:headregistration is whatrender-headlooks up. - Guide ch.18 — Routing and Guide ch.19 — Routing reference — narrative coverage including nav-token semantics,
:can-leaveflows, query strings, and multi-frame routing. - Spec 012 — Routing — the normative source.