@rangojs/router 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/README.md +254 -89
  2. package/dist/testing/vitest.js +252 -0
  3. package/dist/types/browser/navigation-transaction.d.ts +6 -0
  4. package/dist/types/browser/partial-update.d.ts +11 -0
  5. package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
  6. package/dist/types/browser/rsc-router.d.ts +1 -1
  7. package/dist/types/browser/scroll-restoration.d.ts +7 -0
  8. package/dist/types/cache/cache-exec-scope.d.ts +18 -1
  9. package/dist/types/cache/cache-scope.d.ts +34 -5
  10. package/dist/types/cache/handle-capture.d.ts +41 -3
  11. package/dist/types/cache/handle-snapshot.d.ts +53 -13
  12. package/dist/types/cache/segment-codec.d.ts +10 -3
  13. package/dist/types/cache/taint.d.ts +1 -1
  14. package/dist/types/cache/types.d.ts +15 -3
  15. package/dist/types/client.d.ts +4 -4
  16. package/dist/types/client.rsc.d.ts +4 -5
  17. package/dist/types/components/DefaultDocument.d.ts +5 -2
  18. package/dist/types/context-var.d.ts +6 -5
  19. package/dist/types/escape-script.d.ts +1 -1
  20. package/dist/types/handles/MetaTags.d.ts +5 -4
  21. package/dist/types/handles/Scripts.d.ts +8 -7
  22. package/dist/types/handles/meta.d.ts +2 -2
  23. package/dist/types/handles/script.d.ts +17 -16
  24. package/dist/types/href-client.d.ts +1 -1
  25. package/dist/types/html.d.ts +12 -0
  26. package/dist/types/index.d.ts +1 -1
  27. package/dist/types/index.rsc.d.ts +3 -2
  28. package/dist/types/loader-store.d.ts +7 -4
  29. package/dist/types/reverse.d.ts +36 -4
  30. package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
  31. package/dist/types/route-definition/helpers-types.d.ts +33 -9
  32. package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
  33. package/dist/types/route-types.d.ts +1 -1
  34. package/dist/types/router/debug-manifest.d.ts +3 -2
  35. package/dist/types/router/error-handling.d.ts +20 -0
  36. package/dist/types/router/handler-context.d.ts +6 -0
  37. package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
  38. package/dist/types/router/middleware-types.d.ts +3 -2
  39. package/dist/types/router/router-context.d.ts +3 -1
  40. package/dist/types/router/router-interfaces.d.ts +24 -2
  41. package/dist/types/router/router-options.d.ts +8 -5
  42. package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
  43. package/dist/types/router/segment-resolution/loader-cache.d.ts +37 -1
  44. package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
  45. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +3 -3
  46. package/dist/types/router/segment-resolution/mask-nested.d.ts +7 -7
  47. package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
  48. package/dist/types/router/segment-wrappers.d.ts +1 -1
  49. package/dist/types/rsc/response-route-handler.d.ts +1 -1
  50. package/dist/types/rsc/rsc-rendering.d.ts +1 -2
  51. package/dist/types/rsc/shell-capture-constants.d.ts +8 -0
  52. package/dist/types/rsc/shell-capture.d.ts +5 -4
  53. package/dist/types/rsc/ssr-setup.d.ts +6 -6
  54. package/dist/types/rsc/types.d.ts +10 -0
  55. package/dist/types/server/context.d.ts +33 -3
  56. package/dist/types/server/handle-store.d.ts +26 -2
  57. package/dist/types/server/request-context.d.ts +24 -21
  58. package/dist/types/ssr/index.d.ts +15 -0
  59. package/dist/types/static-handler.d.ts +4 -1
  60. package/dist/types/testing/generated-routes.d.ts +2 -2
  61. package/dist/types/testing/internal/context.d.ts +1 -1
  62. package/dist/types/testing/render-route.d.ts +32 -6
  63. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
  64. package/dist/types/testing/vitest.d.ts +35 -1
  65. package/dist/types/theme/ThemeScript.d.ts +3 -3
  66. package/dist/types/types/cache-types.d.ts +14 -10
  67. package/dist/types/types/handler-context.d.ts +14 -0
  68. package/dist/types/types/loader-types.d.ts +8 -6
  69. package/dist/types/urls/response-types.d.ts +3 -12
  70. package/dist/types/use-loader.d.ts +12 -3
  71. package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
  72. package/dist/types/vite/rango.d.ts +1 -1
  73. package/dist/vite/index.js +1381 -1346
  74. package/package.json +2 -2
  75. package/skills/api-client/SKILL.md +22 -10
  76. package/skills/breadcrumbs/SKILL.md +65 -22
  77. package/skills/bundle-analysis/SKILL.md +22 -22
  78. package/skills/cache-guide/SKILL.md +207 -97
  79. package/skills/caching/SKILL.md +295 -93
  80. package/skills/catalog.json +16 -16
  81. package/skills/client-urls/SKILL.md +53 -31
  82. package/skills/cloudflare/SKILL.md +72 -20
  83. package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
  84. package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
  85. package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
  86. package/skills/comparison/SKILL.md +5 -0
  87. package/skills/comparison/references/framework-comparison.md +39 -33
  88. package/skills/composability/SKILL.md +38 -29
  89. package/skills/css/SKILL.md +10 -13
  90. package/skills/debug-manifest/SKILL.md +74 -31
  91. package/skills/defer-hydration/SKILL.md +2 -1
  92. package/skills/deployment-caching/SKILL.md +6 -1
  93. package/skills/document-cache/SKILL.md +92 -28
  94. package/skills/fonts/SKILL.md +16 -8
  95. package/skills/handler-use/SKILL.md +53 -17
  96. package/skills/hooks/SKILL.md +28 -20
  97. package/skills/hooks/data.md +23 -3
  98. package/skills/hooks/handle-and-actions.md +31 -16
  99. package/skills/hooks/navigation.md +39 -10
  100. package/skills/hooks/outlets.md +13 -6
  101. package/skills/hooks/state.md +37 -15
  102. package/skills/hooks/urls.md +7 -5
  103. package/skills/host-router/SKILL.md +64 -12
  104. package/skills/i18n/SKILL.md +60 -34
  105. package/skills/intercept/SKILL.md +161 -26
  106. package/skills/layout/SKILL.md +106 -50
  107. package/skills/links/SKILL.md +51 -19
  108. package/skills/loader/SKILL.md +242 -106
  109. package/skills/middleware/SKILL.md +119 -23
  110. package/skills/migrate-nextjs/SKILL.md +100 -45
  111. package/skills/migrate-nextjs/backend-host-swap.md +5 -2
  112. package/skills/migrate-react-router/SKILL.md +49 -38
  113. package/skills/migrate-react-router/component-migration.md +25 -6
  114. package/skills/migrate-react-router/data-and-actions.md +18 -5
  115. package/skills/migrate-react-router/route-mapping.md +12 -6
  116. package/skills/mime-routes/SKILL.md +12 -7
  117. package/skills/observability/SKILL.md +28 -14
  118. package/skills/parallel/SKILL.md +75 -39
  119. package/skills/ppr/SKILL.md +345 -150
  120. package/skills/prerender/SKILL.md +189 -100
  121. package/skills/rango/SKILL.md +65 -34
  122. package/skills/react-compiler/SKILL.md +6 -5
  123. package/skills/response-routes/SKILL.md +133 -25
  124. package/skills/route/SKILL.md +165 -60
  125. package/skills/router-setup/SKILL.md +418 -240
  126. package/skills/scripts/SKILL.md +31 -20
  127. package/skills/server-actions/SKILL.md +220 -59
  128. package/skills/shell-manifest/SKILL.md +12 -7
  129. package/skills/streams-and-websockets/SKILL.md +32 -14
  130. package/skills/tailwind/SKILL.md +37 -33
  131. package/skills/testing/SKILL.md +68 -32
  132. package/skills/testing/cache-prerender.md +55 -29
  133. package/skills/testing/client-components.md +59 -6
  134. package/skills/testing/e2e-parity.md +12 -2
  135. package/skills/testing/flight.md +19 -11
  136. package/skills/testing/handles.md +8 -5
  137. package/skills/testing/loader.md +8 -5
  138. package/skills/testing/middleware.md +8 -3
  139. package/skills/testing/predicates.md +120 -0
  140. package/skills/testing/render-handler.md +70 -22
  141. package/skills/testing/response-routes.md +11 -4
  142. package/skills/testing/reverse-and-types.md +20 -14
  143. package/skills/testing/server-actions.md +12 -3
  144. package/skills/testing/server-tree.md +9 -0
  145. package/skills/testing/setup.md +9 -5
  146. package/skills/theme/SKILL.md +106 -29
  147. package/skills/typesafety/SKILL.md +20 -9
  148. package/skills/typesafety/env-and-bindings.md +126 -47
  149. package/skills/typesafety/generated-files-and-cli.md +26 -14
  150. package/skills/typesafety/params-and-search.md +44 -19
  151. package/skills/typesafety/route-types.md +50 -14
  152. package/skills/use-cache/SKILL.md +200 -82
  153. package/skills/vercel/SKILL.md +21 -11
  154. package/skills/view-transitions/SKILL.md +41 -20
  155. package/src/__augment-tests__/augmented.check.ts +26 -1
  156. package/src/browser/index.ts +2 -2
  157. package/src/browser/navigation-bridge.ts +3 -4
  158. package/src/browser/navigation-transaction.ts +22 -2
  159. package/src/browser/partial-update.ts +35 -25
  160. package/src/browser/react/ScrollRestoration.tsx +6 -6
  161. package/src/browser/rsc-router.tsx +1 -1
  162. package/src/browser/scroll-restoration.ts +53 -6
  163. package/src/cache/cache-exec-scope.ts +37 -4
  164. package/src/cache/cache-key-utils.ts +1 -6
  165. package/src/cache/cache-runtime.ts +230 -61
  166. package/src/cache/cache-scope.ts +76 -12
  167. package/src/cache/document-cache.ts +15 -0
  168. package/src/cache/handle-capture.ts +90 -10
  169. package/src/cache/handle-snapshot.ts +132 -28
  170. package/src/cache/segment-codec.ts +26 -7
  171. package/src/cache/tag-invalidation.ts +4 -3
  172. package/src/cache/taint.ts +1 -1
  173. package/src/cache/types.ts +16 -3
  174. package/src/client.rsc.tsx +10 -9
  175. package/src/client.tsx +5 -4
  176. package/src/components/DefaultDocument.tsx +7 -2
  177. package/src/context-var.ts +6 -5
  178. package/src/escape-script.ts +1 -1
  179. package/src/handles/MetaTags.tsx +13 -11
  180. package/src/handles/Scripts.tsx +11 -10
  181. package/src/handles/meta.ts +2 -2
  182. package/src/handles/script.ts +17 -16
  183. package/src/href-client.ts +1 -1
  184. package/src/html.ts +12 -0
  185. package/src/index.rsc.ts +5 -0
  186. package/src/index.ts +1 -0
  187. package/src/loader-store.ts +7 -4
  188. package/src/prerender/build-shell-capture.ts +8 -11
  189. package/src/reverse.ts +44 -4
  190. package/src/route-definition/dsl-helpers.ts +117 -30
  191. package/src/route-definition/helpers-types.ts +33 -9
  192. package/src/route-definition/resolve-handler-use.ts +121 -6
  193. package/src/route-types.ts +0 -3
  194. package/src/router/debug-manifest.ts +10 -2
  195. package/src/router/error-handling.ts +51 -15
  196. package/src/router/handler-context.ts +34 -25
  197. package/src/router/intercept-resolution.ts +125 -39
  198. package/src/router/loader-resolution.ts +56 -11
  199. package/src/router/match-api.ts +12 -17
  200. package/src/router/match-middleware/background-revalidation.ts +161 -96
  201. package/src/router/match-middleware/cache-lookup.ts +93 -10
  202. package/src/router/match-middleware/cache-store.ts +17 -90
  203. package/src/router/middleware-types.ts +3 -5
  204. package/src/router/middleware.ts +52 -9
  205. package/src/router/prerender-match.ts +53 -19
  206. package/src/router/route-snapshot.ts +11 -1
  207. package/src/router/router-context.ts +2 -0
  208. package/src/router/router-interfaces.ts +25 -2
  209. package/src/router/router-options.ts +8 -5
  210. package/src/router/segment-resolution/fresh.ts +44 -12
  211. package/src/router/segment-resolution/loader-cache.ts +205 -45
  212. package/src/router/segment-resolution/loader-mask.ts +12 -22
  213. package/src/router/segment-resolution/loader-snapshot.ts +6 -6
  214. package/src/router/segment-resolution/mask-nested.ts +7 -7
  215. package/src/router/segment-resolution/revalidation.ts +34 -6
  216. package/src/router/segment-wrappers.ts +4 -0
  217. package/src/rsc/render-pipeline.ts +3 -0
  218. package/src/rsc/response-route-handler.ts +11 -3
  219. package/src/rsc/rsc-rendering.ts +8 -45
  220. package/src/rsc/shell-capture-constants.ts +13 -0
  221. package/src/rsc/shell-capture.ts +111 -90
  222. package/src/rsc/ssr-setup.ts +14 -8
  223. package/src/rsc/types.ts +11 -0
  224. package/src/server/context.ts +88 -11
  225. package/src/server/cookie-store.ts +5 -4
  226. package/src/server/handle-store.ts +232 -7
  227. package/src/server/request-context.ts +35 -30
  228. package/src/ssr/index.tsx +40 -7
  229. package/src/static-handler.ts +4 -1
  230. package/src/testing/flight-runtime.d.ts +21 -0
  231. package/src/testing/generated-routes.ts +2 -2
  232. package/src/testing/internal/context.ts +1 -1
  233. package/src/testing/internal/flight-client-globals.ts +7 -5
  234. package/src/testing/render-route.tsx +85 -24
  235. package/src/testing/run-loader.ts +5 -1
  236. package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
  237. package/src/testing/vitest.ts +62 -1
  238. package/src/theme/ThemeScript.tsx +3 -3
  239. package/src/types/cache-types.ts +14 -10
  240. package/src/types/handler-context.ts +14 -0
  241. package/src/types/loader-types.ts +8 -6
  242. package/src/urls/response-types.ts +3 -23
  243. package/src/use-loader.tsx +12 -3
  244. package/src/vite/plugins/use-cache-transform.ts +118 -74
  245. package/src/vite/rango.ts +7 -3
@@ -6,9 +6,12 @@ argument-hint: [ctx.reverse|href|useHref|useMount|useReverse|scopedReverse]
6
6
 
7
7
  # Links & URL Generation
8
8
 
9
- @rangojs/router provides different href APIs for server and client contexts.
9
+ Use this skill to generate URLs to your own routes by name (server) or by
10
+ typed path (client), and to keep links correct when a module is mounted under
11
+ a different `include()` prefix. For route typing setup see `/typesafety`; for
12
+ links to JSON/text endpoints see `/response-routes`.
10
13
 
11
- **Default server API: `ctx.reverse()`.** Generate URLs from the handler context — it's typed, auto-fills mount params, and resolves local (`.name`) and absolute (`name.sub`) names.
14
+ **Default server API: `ctx.reverse()`.** Generate URLs from the handler context — it's typed, auto-fills params from the current request, and resolves local (`.name`) and global (`name`) names. In middleware and response routes it takes global names only (see below).
12
15
 
13
16
  **On the client, two patterns:**
14
17
 
@@ -19,10 +22,19 @@ argument-hint: [ctx.reverse|href|useHref|useMount|useReverse|scopedReverse]
19
22
 
20
23
  ## Server: ctx.reverse()
21
24
 
22
- Available in route handlers via HandlerContext. Resolves named routes using the full route map. This is the default way to generate URLs on the server.
25
+ Available on the handler context (route handlers, layouts, parallels), on
26
+ middleware and loader contexts, and on `getRequestContext()` inside server
27
+ actions. Resolves named routes using the full route map. This is the default
28
+ way to generate URLs on the server. Patterns in the map already include the
29
+ router `basename`, so results are basename-prefixed.
30
+
31
+ Middleware and response routes (`path.json()`, `path.text()`, …) get a
32
+ global-only `ctx.reverse` (`GlobalReverseFunction`): no `include()` scope and no
33
+ param auto-fill, so pass the fully qualified name and every param. A dot-local
34
+ `.name` is a type error there. See `/middleware` and `/response-routes`.
23
35
 
24
36
  ```typescript
25
- import { urls, scopedReverse } from "@rangojs/router";
37
+ import { urls } from "@rangojs/router";
26
38
 
27
39
  export const shopPatterns = urls(({ path, layout }) => [
28
40
  layout(<ShopLayout />, () => [
@@ -63,7 +75,7 @@ path("/product/:slug", (ctx) => {
63
75
 
64
76
  ### Local names (dot-prefixed)
65
77
 
66
- Prefix a name with `.` to resolve it within the current `include()` scope. The route is looked up using the include's mount namespace.
78
+ Prefix a name with `.` to resolve it within the current `include()` scope. The route is looked up using the include's mount namespace. If the name is not found there, lookup walks up the enclosing include scopes (`shop.admin.x`, then `shop.x`). It falls back to a bare root-level name only for root-scoped routes (top-level routes and routes under `{ name: "" }` mounts); inside a named mount, a dot-local name never leaks into unrelated global names.
67
79
 
68
80
  ```typescript
69
81
  // urls/magazine.tsx — mounted at include("/magazine", magazinePatterns, { name: "magazine" })
@@ -97,7 +109,7 @@ export const tenantPatterns = urls(({ path }) => [
97
109
  ]);
98
110
  ```
99
111
 
100
- Auto-fill uses `{ ...ctx.params, ...hrefParams }` — current request params are defaults, explicit params win. Params not needed by the target route are silently ignored.
112
+ Auto-fill uses `{ ...ctx.params, ...hrefParams }` — current request params are defaults, explicit params win. Params not needed by the target route are silently ignored. Because every current param is a default, drop an optional one explicitly with `undefined` or `""` — under `include("/:locale?", ...)` on `/en/...`, a bare `ctx.reverse("menu.index")` keeps `/en`, while `ctx.reverse("menu.index", { locale: undefined })` returns `/` (see `/i18n`). A missing required param throws `Missing param "slug" for route "…"`; an unknown name throws `Unknown route: "…"`.
101
113
 
102
114
  ### Global names (unprefixed)
103
115
 
@@ -112,7 +124,7 @@ Unprefixed names resolve against the full named-routes map (the generated `route
112
124
 
113
125
  ### reverse with search params
114
126
 
115
- When a route has a `search` schema, pass a typed search object as the third argument:
127
+ When a route has a `search` schema, pass a typed search object as the third argument. `undefined`/`null` values are skipped; keys keep insertion order:
116
128
 
117
129
  ```typescript
118
130
  path("/search", (ctx) => {
@@ -124,7 +136,7 @@ path("/search", (ctx) => {
124
136
 
125
137
  ### scopedReverse() - type-safe ctx.reverse
126
138
 
127
- Wraps `ctx.reverse` with local route type information for autocomplete and validation. Runtime behavior is identical to `ctx.reverse` — `scopedReverse` is a type-only cast. The same dot-prefix rule applies: local names use `.name`, global names use `name.sub`.
139
+ Wraps `ctx.reverse` with the module's local route types for autocomplete and validation. Runtime behavior is identical to `ctx.reverse` — `scopedReverse` is a type-only cast, and it also works on loader contexts. Don't use it on middleware or response-route contexts: their `ctx.reverse` has no `include()` scope, so the local names it types would throw `Unknown route`. Always call local names with the dot: the returned function is typed only against the module's local map, so an unprefixed local name (`reverse("cart")`) type-checks but is resolved as a global name at runtime — it throws `Unknown route` unless a global route of that exact name happens to exist.
128
140
 
129
141
  ```typescript
130
142
  import { scopedReverse } from "@rangojs/router";
@@ -134,12 +146,15 @@ path("/product/:slug", (ctx) => {
134
146
 
135
147
  reverse(".cart"); // Local name (dot-prefixed) — resolves in include scope
136
148
  reverse(".product", { slug: "widget" }); // Local name with params
137
- reverse("blog.post", { slug: "hi" }); // Global name (dotted) — full route map
149
+
150
+ ctx.reverse("blog.post", { slug: "hi" }); // Global names: call ctx.reverse directly
138
151
 
139
152
  return <ProductPage slug={ctx.params.slug} />;
140
153
  }, { name: "product" })
141
154
  ```
142
155
 
156
+ To type local and global names on one function, type the handler with the module's generated map instead: `Handler<"shop.product", routes>` (see `/typesafety`).
157
+
143
158
  `reverse()` does not accept raw path strings (`"/about"`). For static paths in client components, use `href("/about")`; on the server, look up the route by name.
144
159
 
145
160
  ## Client components: receive URLs as props
@@ -150,9 +165,12 @@ path("/product/:slug", (ctx) => {
150
165
 
151
166
  ```tsx
152
167
  // server
153
- function BlogPostPage(ctx: HandlerContext) {
168
+ import type { Handler } from "@rangojs/router";
169
+ import { ShareButton } from "./share-button";
170
+
171
+ export const BlogPostPage: Handler<{ slug: string }> = (ctx) => {
154
172
  return <ShareButton url={ctx.reverse(".post", { slug: ctx.params.slug })} />;
155
- }
173
+ };
156
174
  ```
157
175
 
158
176
  ```tsx
@@ -169,6 +187,8 @@ export function ShareButton({ url }: { url: string }) {
169
187
 
170
188
  ```tsx
171
189
  // server — loaders/nav.ts
190
+ import { createLoader } from "@rangojs/router";
191
+
172
192
  export const NavLoader = createLoader((ctx) => ({
173
193
  home: ctx.reverse("home"),
174
194
  blog: ctx.reverse("blog.index"),
@@ -182,6 +202,8 @@ const urlpatterns = urls(({ path, loader }) => [
182
202
 
183
203
  ```tsx
184
204
  "use client";
205
+ import { Link, useLoader } from "@rangojs/router/client";
206
+ import { NavLoader } from "../loaders/nav";
185
207
 
186
208
  function Nav() {
187
209
  const { data } = useLoader(NavLoader);
@@ -195,6 +217,7 @@ function Nav() {
195
217
 
196
218
  ```tsx
197
219
  "use server";
220
+ import { getRequestContext } from "@rangojs/router";
198
221
 
199
222
  export async function getProductUrl(slug: string) {
200
223
  const ctx = getRequestContext();
@@ -237,7 +260,9 @@ import { href } from "@rangojs/router/client";
237
260
  export const appHref = (path: Rango.Path): string => href(path);
238
261
  ```
239
262
 
240
- `href()` is a raw path helper — it is **not** basename-aware. It returns the path as-is (or with the include mount prefix via `useHref()`). For basename-aware navigation, use `Link`, `useRouter().push()`, or `reverse()`, which auto-prefix root-relative paths with the router's basename.
263
+ `href()` is a raw path helper — it is **not** basename-aware. It returns the path as-is (or with the include mount prefix via `useHref()`). `<Link to={href("/about")}>` still lands on the basename-prefixed URL because `Link` and `useRouter().push()` prefix root-relative paths with the router's basename; a plain `<a href>`, `fetch()`, or `window.location` gets the raw path. On the server, `ctx.reverse()` output is already basename-prefixed.
264
+
265
+ `href.json()`, `href.text()`, `href.html()`, `href.xml()`, `href.md()`, `href.image()`, `href.stream()` and `href.any()` return `{ to, "data-external": "" }` props for linking to response routes with a hard navigation — see `/response-routes`.
241
266
 
242
267
  ## Client: useHref()
243
268
 
@@ -263,6 +288,8 @@ function ShopNav() {
263
288
 
264
289
  Use `useHref()` for local navigation within a mounted module. Use the bare `href()` function for absolute paths outside the current mount.
265
290
 
291
+ `useHref()` concatenates the mount **pattern** as-is and does not substitute params, and its argument is not checked against your routes. Under a parameterized mount (`include("/tenant/:tenantId", ...)`) it would produce `/tenant/:tenantId/cart` — use `useReverse(routes)` there, which substitutes mount params from `useParams()`.
292
+
266
293
  ## Client: useMount()
267
294
 
268
295
  Returns the current `include()` mount path. Useful for building custom logic based on mount location.
@@ -279,7 +306,7 @@ function MountInfo() {
279
306
  }
280
307
  ```
281
308
 
282
- `useMount()` reads from `MountContext`, which is automatically set by `include()` in the segment tree.
309
+ `useMount()` reads from `MountContext`, which is automatically set by `include()` in the segment tree. Nested includes return the nearest mount's full URL prefix (e.g. `/shop/admin`), and a parameterized include returns its pattern (`/tenant/:tenantId`), not the matched URL. `href(path, mount)` is the non-hook equivalent of `useHref()`.
283
310
 
284
311
  ## Client: useReverse(routes)
285
312
 
@@ -309,7 +336,9 @@ export function BlogNav() {
309
336
  1. Strips an optional leading `.` and looks up the name in the imported `routes` map.
310
337
  2. Joins the local pattern with the surrounding `useMount()` value — the include's URL pattern.
311
338
  3. Substitutes params: explicit params from the call, then auto-filled from `useParams()` for anything still unresolved (mount params like `:tenantId` flow in this way).
312
- 4. Appends a query string if a search object is passed and the route has a `search` schema.
339
+ 4. Appends a query string when a search object is passed (typed from the route's `search` schema; `undefined`/`null` values are skipped).
340
+
341
+ The returned function is memoized on the routes map, mount, and current params, so it is safe to pass to memoized children.
313
342
 
314
343
  ### Mount-relativity
315
344
 
@@ -337,13 +366,16 @@ The `/` pattern under a non-root mount collapses cleanly: under `/news`, `revers
337
366
  When the include itself carries `:params`, those are auto-filled from `useParams()` so the caller doesn't have to thread them through:
338
367
 
339
368
  ```typescript
369
+ // urls/tenant.tsx: index "/" and post "/posts/:postId"
340
370
  // urls.tsx
341
- include("/tenant/:tenantId", clientReversePatterns, { name: "tenant" });
371
+ include("/tenant/:tenantId", tenantPatterns, { name: "tenant" });
342
372
  ```
343
373
 
344
374
  ```tsx
375
+ import { routes as tenantRoutes } from "../urls/tenant.gen.js";
376
+
345
377
  // At /tenant/acme/posts/p1, useParams() = { tenantId: "acme", postId: "p1" }
346
- const reverse = useReverse(clientReverseRoutes);
378
+ const reverse = useReverse(tenantRoutes);
347
379
 
348
380
  reverse(".index"); // "/tenant/acme"
349
381
  reverse(".post", { postId: "p2" }); // "/tenant/acme/posts/p2" (tenantId auto-filled)
@@ -411,7 +443,7 @@ pnpm exec rango generate src/urls --static
411
443
 
412
444
  Don't edit the file by hand — re-run codegen when patterns change.
413
445
 
414
- **Today the Vite plugin only regenerates the router-level `*.named-routes.gen.ts`.** Per-module `urls/*.gen.ts` files are emitted only by the CLI (or `writePerModuleRouteTypesForFile` programmatically). Commit the generated files and re-run `rango generate` whenever a `urls()` module's `path()`/`include()` shape changes. A common workflow is to wire it into a `predev` script:
446
+ **The Vite plugin only regenerates the router-level `*.named-routes.gen.ts`.** Per-module `urls/*.gen.ts` files are emitted only by the `rango generate` CLI. Commit the generated files and re-run `rango generate` whenever a `urls()` module's `path()`/`include()` shape changes. `clientUrls()` modules get a `.gen.ts` the same way. A common workflow is to wire it into a `predev` script:
415
447
 
416
448
  ```jsonc
417
449
  // package.json
@@ -452,7 +484,7 @@ queued or in flight; removing it re-arms both Link types.
452
484
  | Context | API | Resolves | Use for |
453
485
  | ---------------- | -------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------- |
454
486
  | Server handler | `ctx.reverse("name")` | Named routes (local + absolute) | **Default** server-side URL generation |
455
- | Server handler | `scopedReverse<T>(ctx.reverse)` | Same, with type safety | Type-safe server URLs |
487
+ | Server handler | `scopedReverse<T>(ctx.reverse)` | Local `.name` routes, typed from `T` | Type-safe in-module server URLs |
456
488
  | Client component | `useReverse(routes)` | Local names from an imported `routes` map | Typed in-module URL generation without round-tripping the server |
457
489
  | Client component | (URL passed as prop / loader data / action return) | Named routes | Cross-module URLs or one-off names you don't want to import |
458
490
  | Client component | `href("/path")` | Absolute paths (static strings) | Static navigation where no named-route lookup is needed |
@@ -470,7 +502,7 @@ import { urls, scopedReverse } from "@rangojs/router";
470
502
  export const shopPatterns = urls(({ path, layout }) => [
471
503
  layout((ctx) => {
472
504
  const reverse = scopedReverse<typeof shopPatterns>(ctx.reverse);
473
- return <ShopLayout cartUrl={reverse("cart")} />;
505
+ return <ShopLayout cartUrl={reverse(".cart")} />;
474
506
  }, () => [
475
507
  path("/", ShopIndex, { name: "index" }),
476
508
  path("/cart", CartPage, { name: "cart" }),