Status: active (Phase 1 complete, Phase 2 pending) Priority: P1 Created: 2026-02-11 Owner: conductor
The current routing system has two separate URL encoding mechanisms that overlap and conflict:
route_url.cljc uses a _sc_ query parameter with base64-encoded transit for per-state paramsroute_history.cljc uses a _rp_ query parameter with base64-encoded transit for route paramsAdditionally, route paths are flat — each rstate declares its complete path (e.g., [:route/path ["users" "edit"]]). There is no automatic composition where a child route inherits its parent's path prefix, and no support for parameterized path segments (e.g., /users/:id/edit).
This spec designs a unified URL format and hierarchical path composition for the routing system.
From route_url.cljc:
https://app.com/users/edit?_sc_=<base64-transit of {state-id {param-key param-val}}>
From route_history.cljc:
https://app.com/users/edit?_rp_=<base64-transit of {param-key param-val}>
Both are opaque blobs. Neither supports human-readable query parameters or parameterized path segments.
rstate with :route/path ["edit"] under a parent with :route/path ["users"] produces URL path /users/edit:route/path ["users" :id "edit"] where :id is resolved from the route's data model or event data/users/42/edit matches ["users" :id "edit"] and extracts {:id "42"}:route/path inherits its parent's path (useful for istate wrappers)_sc_ and _rp_ into a single encoding scheme. Opaque per-state params (_sc_ style, keyed by state ID) are the right model since multiple states contribute params simultaneously.:route/query-params {:q :search-term} maps data model key :search-term to URL param ?q=value/<path-segments>?<named-params>&_s=<opaque-state-params>#<optional-hash>_s) should be optional — only present when states declare :route/params:route/path with full paths must continue to workroute->url and url->route callbacks on new-html5-history remain the extension point for custom URL schemesintegration/fulcro/ui_routes.cljc — rstate, istate, routes, establish-route-params-node, apply-external-route, state-for-pathintegration/fulcro/route_url.cljc — Unify with route_history URL functions, add path matchingintegration/fulcro/route_history.cljc — Unify URL encoding, update route->url/url->route defaultsAdd a resolve-full-path function that walks the statechart element tree from a state up to the routing root, collecting path segments:
;; Given:
(routes {:routing/root :app/Root :id :routes}
(rstate {:route/target :user/List :route/path ["users"]}
(rstate {:route/target :user/Detail :route/path [:id]}
(rstate {:route/target :user/Settings :route/path ["settings"]}))))
;; resolve-full-path for :user/Settings => ["users" :id "settings"]
During apply-external-route, path matching resolves parameterized segments:
;; URL: /users/42/settings
;; Pattern: ["users" :id "settings"]
;; Extracted: {:id "42"}
When entering a state (on-entry), parameterized segments are resolved from:
/users/42/settings?q=dark&_s=eyAuLi4gfQ==
├── path segments (including resolved params)
├── named query params (declared via :route/query-params)
└── opaque state params blob (base64 transit, keyed by state ID)
Phase 1: Add resolve-full-path and parameterized matching. Existing flat paths work unchanged.
Phase 2: Unify URL encoding. Deprecate _sc_ and _rp_ in favor of _s + named params.
Phase 3: Remove old encoding support in a future version.
:id) or tagged vectors ([:param :id])? Keywords are concise but could conflict with literal path segments that happen to be keywords.["users"] + child ["edit"] = /users/edit["users" :id] with {:id 42} produces /users/42/users/42 matched against ["users" :id] yields {:id "42"}:route/path inherit parent path:route/path (existing behavior) still works unchangedroute->url / url->route callbacks still work for custom schemes_sc_ and _rp_ unified into single _s parameterCan you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |