@rangojs/router 0.16.0 → 0.18.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 (309) hide show
  1. package/README.md +255 -89
  2. package/dist/testing/vitest.js +252 -0
  3. package/dist/types/browser/cookie-name.d.ts +6 -0
  4. package/dist/types/browser/history-state.d.ts +16 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +6 -0
  6. package/dist/types/browser/partial-update.d.ts +11 -0
  7. package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
  8. package/dist/types/browser/rsc-router.d.ts +1 -1
  9. package/dist/types/browser/scroll-restoration.d.ts +7 -0
  10. package/dist/types/cache/cache-exec-scope.d.ts +18 -1
  11. package/dist/types/cache/cache-key-utils.d.ts +17 -2
  12. package/dist/types/cache/cache-policy.d.ts +17 -0
  13. package/dist/types/cache/cache-scope.d.ts +132 -19
  14. package/dist/types/cache/cache-tag.d.ts +92 -7
  15. package/dist/types/cache/cf/cf-base64.d.ts +6 -0
  16. package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
  17. package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
  18. package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
  19. package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
  20. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
  21. package/dist/types/cache/handle-capture.d.ts +41 -3
  22. package/dist/types/cache/handle-snapshot.d.ts +54 -13
  23. package/dist/types/cache/index.d.ts +1 -0
  24. package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
  25. package/dist/types/cache/segment-codec.d.ts +10 -3
  26. package/dist/types/cache/shell-memo.d.ts +178 -0
  27. package/dist/types/cache/shell-snapshot.d.ts +110 -21
  28. package/dist/types/cache/tag-invalidation.d.ts +30 -3
  29. package/dist/types/cache/taint.d.ts +1 -1
  30. package/dist/types/cache/types.d.ts +220 -44
  31. package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
  32. package/dist/types/client.d.ts +4 -4
  33. package/dist/types/client.rsc.d.ts +4 -5
  34. package/dist/types/components/DefaultDocument.d.ts +5 -2
  35. package/dist/types/context-var.d.ts +6 -5
  36. package/dist/types/escape-script.d.ts +1 -1
  37. package/dist/types/handles/MetaTags.d.ts +5 -4
  38. package/dist/types/handles/Scripts.d.ts +8 -7
  39. package/dist/types/handles/meta.d.ts +2 -2
  40. package/dist/types/handles/script.d.ts +17 -16
  41. package/dist/types/href-client.d.ts +1 -1
  42. package/dist/types/html.d.ts +12 -0
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/index.rsc.d.ts +3 -2
  45. package/dist/types/loader-store.d.ts +7 -4
  46. package/dist/types/prerender/build-shell-capture.d.ts +11 -4
  47. package/dist/types/reverse.d.ts +36 -4
  48. package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
  49. package/dist/types/route-definition/helpers-types.d.ts +33 -9
  50. package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
  51. package/dist/types/route-types.d.ts +1 -1
  52. package/dist/types/router/debug-manifest.d.ts +3 -2
  53. package/dist/types/router/error-handling.d.ts +20 -0
  54. package/dist/types/router/handler-context.d.ts +6 -0
  55. package/dist/types/router/instrument.d.ts +8 -0
  56. package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
  57. package/dist/types/router/middleware-types.d.ts +9 -3
  58. package/dist/types/router/router-context.d.ts +3 -1
  59. package/dist/types/router/router-interfaces.d.ts +24 -2
  60. package/dist/types/router/router-options.d.ts +8 -5
  61. package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
  62. package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
  63. package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
  64. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
  65. package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
  66. package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
  67. package/dist/types/router/segment-wrappers.d.ts +1 -1
  68. package/dist/types/rsc/full-payload.d.ts +14 -0
  69. package/dist/types/rsc/handler.d.ts +1 -1
  70. package/dist/types/rsc/response-route-handler.d.ts +1 -1
  71. package/dist/types/rsc/rsc-rendering.d.ts +1 -2
  72. package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
  73. package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
  74. package/dist/types/rsc/shell-capture.d.ts +151 -19
  75. package/dist/types/rsc/shell-serve.d.ts +140 -17
  76. package/dist/types/rsc/ssr-setup.d.ts +6 -6
  77. package/dist/types/rsc/types.d.ts +17 -0
  78. package/dist/types/server/context.d.ts +139 -15
  79. package/dist/types/server/cookie-store.d.ts +21 -0
  80. package/dist/types/server/handle-store.d.ts +26 -2
  81. package/dist/types/server/request-context.d.ts +182 -53
  82. package/dist/types/ssr/index.d.ts +24 -0
  83. package/dist/types/ssr/ssr-root.d.ts +9 -8
  84. package/dist/types/static-handler.d.ts +4 -1
  85. package/dist/types/testing/flight.entry.d.ts +7 -0
  86. package/dist/types/testing/generated-routes.d.ts +2 -2
  87. package/dist/types/testing/internal/context.d.ts +1 -1
  88. package/dist/types/testing/render-route.d.ts +32 -6
  89. package/dist/types/testing/run-loader.d.ts +18 -3
  90. package/dist/types/testing/serve-shell-request.d.ts +132 -0
  91. package/dist/types/testing/shell-status.d.ts +23 -10
  92. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
  93. package/dist/types/testing/vitest.d.ts +35 -1
  94. package/dist/types/theme/ThemeScript.d.ts +3 -3
  95. package/dist/types/types/cache-types.d.ts +14 -10
  96. package/dist/types/types/handler-context.d.ts +28 -1
  97. package/dist/types/types/loader-types.d.ts +8 -6
  98. package/dist/types/urls/pattern-types.d.ts +22 -18
  99. package/dist/types/urls/response-types.d.ts +3 -12
  100. package/dist/types/use-loader.d.ts +12 -3
  101. package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
  102. package/dist/types/vite/rango.d.ts +1 -1
  103. package/dist/types/vite/router-discovery.d.ts +29 -2
  104. package/dist/vite/index.js +1420 -1348
  105. package/package.json +2 -2
  106. package/skills/api-client/SKILL.md +22 -10
  107. package/skills/breadcrumbs/SKILL.md +75 -22
  108. package/skills/bundle-analysis/SKILL.md +22 -22
  109. package/skills/cache-guide/SKILL.md +306 -124
  110. package/skills/caching/SKILL.md +530 -98
  111. package/skills/catalog.json +16 -16
  112. package/skills/client-urls/SKILL.md +65 -38
  113. package/skills/cloudflare/SKILL.md +87 -20
  114. package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
  115. package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
  116. package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
  117. package/skills/comparison/SKILL.md +5 -0
  118. package/skills/comparison/references/framework-comparison.md +75 -57
  119. package/skills/composability/SKILL.md +38 -29
  120. package/skills/css/SKILL.md +10 -13
  121. package/skills/debug-manifest/SKILL.md +74 -31
  122. package/skills/defer-hydration/SKILL.md +52 -31
  123. package/skills/deployment-caching/SKILL.md +11 -3
  124. package/skills/document-cache/SKILL.md +98 -28
  125. package/skills/fonts/SKILL.md +16 -8
  126. package/skills/handler-use/SKILL.md +55 -17
  127. package/skills/hooks/SKILL.md +28 -20
  128. package/skills/hooks/data.md +23 -3
  129. package/skills/hooks/handle-and-actions.md +34 -17
  130. package/skills/hooks/navigation.md +39 -10
  131. package/skills/hooks/outlets.md +13 -6
  132. package/skills/hooks/state.md +39 -15
  133. package/skills/hooks/urls.md +7 -5
  134. package/skills/host-router/SKILL.md +64 -12
  135. package/skills/i18n/SKILL.md +60 -34
  136. package/skills/intercept/SKILL.md +161 -26
  137. package/skills/layout/SKILL.md +106 -50
  138. package/skills/links/SKILL.md +51 -19
  139. package/skills/loader/SKILL.md +386 -128
  140. package/skills/middleware/SKILL.md +129 -26
  141. package/skills/migrate-nextjs/SKILL.md +156 -70
  142. package/skills/migrate-nextjs/backend-host-swap.md +5 -2
  143. package/skills/migrate-react-router/SKILL.md +51 -39
  144. package/skills/migrate-react-router/component-migration.md +28 -7
  145. package/skills/migrate-react-router/data-and-actions.md +21 -6
  146. package/skills/migrate-react-router/route-mapping.md +19 -10
  147. package/skills/mime-routes/SKILL.md +12 -7
  148. package/skills/observability/SKILL.md +92 -14
  149. package/skills/parallel/SKILL.md +88 -47
  150. package/skills/ppr/SKILL.md +866 -379
  151. package/skills/prerender/SKILL.md +210 -108
  152. package/skills/rango/SKILL.md +115 -65
  153. package/skills/react-compiler/SKILL.md +6 -5
  154. package/skills/response-routes/SKILL.md +133 -25
  155. package/skills/route/SKILL.md +170 -60
  156. package/skills/router-setup/SKILL.md +420 -240
  157. package/skills/scripts/SKILL.md +39 -21
  158. package/skills/server-actions/SKILL.md +231 -59
  159. package/skills/shell-manifest/SKILL.md +28 -14
  160. package/skills/streams-and-websockets/SKILL.md +32 -14
  161. package/skills/tailwind/SKILL.md +37 -33
  162. package/skills/testing/SKILL.md +73 -33
  163. package/skills/testing/cache-prerender.md +60 -33
  164. package/skills/testing/client-components.md +59 -6
  165. package/skills/testing/e2e-parity.md +12 -2
  166. package/skills/testing/flight.md +19 -11
  167. package/skills/testing/handles.md +8 -5
  168. package/skills/testing/loader.md +26 -6
  169. package/skills/testing/middleware.md +8 -3
  170. package/skills/testing/predicates.md +120 -0
  171. package/skills/testing/render-handler.md +70 -22
  172. package/skills/testing/response-routes.md +11 -4
  173. package/skills/testing/reverse-and-types.md +20 -14
  174. package/skills/testing/serve-shell-request.md +188 -0
  175. package/skills/testing/server-actions.md +12 -3
  176. package/skills/testing/server-tree.md +9 -0
  177. package/skills/testing/setup.md +9 -5
  178. package/skills/theme/SKILL.md +155 -30
  179. package/skills/typesafety/SKILL.md +20 -9
  180. package/skills/typesafety/env-and-bindings.md +137 -50
  181. package/skills/typesafety/generated-files-and-cli.md +26 -14
  182. package/skills/typesafety/params-and-search.md +44 -19
  183. package/skills/typesafety/route-types.md +50 -14
  184. package/skills/use-cache/SKILL.md +224 -83
  185. package/skills/vercel/SKILL.md +35 -13
  186. package/skills/view-transitions/SKILL.md +42 -21
  187. package/src/__augment-tests__/augmented.check.ts +26 -1
  188. package/src/browser/cookie-name.ts +23 -0
  189. package/src/browser/history-state.ts +23 -0
  190. package/src/browser/index.ts +2 -2
  191. package/src/browser/navigation-bridge.ts +3 -4
  192. package/src/browser/navigation-transaction.ts +22 -2
  193. package/src/browser/partial-update.ts +35 -25
  194. package/src/browser/react/ScrollRestoration.tsx +6 -6
  195. package/src/browser/rsc-router.tsx +6 -1
  196. package/src/browser/scroll-restoration.ts +53 -6
  197. package/src/cache/cache-exec-scope.ts +37 -4
  198. package/src/cache/cache-key-utils.ts +23 -8
  199. package/src/cache/cache-policy.ts +22 -0
  200. package/src/cache/cache-runtime.ts +264 -68
  201. package/src/cache/cache-scope.ts +419 -43
  202. package/src/cache/cache-tag.ts +375 -11
  203. package/src/cache/cf/cf-base64.ts +36 -4
  204. package/src/cache/cf/cf-cache-constants.ts +12 -0
  205. package/src/cache/cf/cf-cache-store.ts +990 -348
  206. package/src/cache/cf/cf-cache-types.ts +32 -4
  207. package/src/cache/cf/cf-shell-frame.ts +258 -0
  208. package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
  209. package/src/cache/document-cache.ts +15 -0
  210. package/src/cache/handle-capture.ts +90 -10
  211. package/src/cache/handle-snapshot.ts +133 -28
  212. package/src/cache/index.ts +2 -0
  213. package/src/cache/isolate-tag-memo.ts +308 -0
  214. package/src/cache/segment-codec.ts +26 -7
  215. package/src/cache/shell-memo.ts +341 -0
  216. package/src/cache/shell-snapshot.ts +217 -27
  217. package/src/cache/tag-invalidation.ts +108 -22
  218. package/src/cache/taint.ts +1 -1
  219. package/src/cache/types.ts +225 -44
  220. package/src/cache/vercel/vercel-cache-store.ts +545 -61
  221. package/src/client.rsc.tsx +10 -9
  222. package/src/client.tsx +5 -4
  223. package/src/components/DefaultDocument.tsx +7 -2
  224. package/src/context-var.ts +6 -5
  225. package/src/escape-script.ts +1 -1
  226. package/src/handles/MetaTags.tsx +13 -11
  227. package/src/handles/Scripts.tsx +11 -10
  228. package/src/handles/meta.ts +2 -2
  229. package/src/handles/script.ts +17 -16
  230. package/src/href-client.ts +1 -1
  231. package/src/html.ts +12 -0
  232. package/src/index.rsc.ts +5 -0
  233. package/src/index.ts +1 -0
  234. package/src/loader-store.ts +7 -4
  235. package/src/prerender/build-shell-capture.ts +73 -22
  236. package/src/reverse.ts +44 -4
  237. package/src/route-definition/dsl-helpers.ts +121 -33
  238. package/src/route-definition/helpers-types.ts +33 -9
  239. package/src/route-definition/resolve-handler-use.ts +121 -6
  240. package/src/route-types.ts +0 -3
  241. package/src/router/debug-manifest.ts +10 -2
  242. package/src/router/error-handling.ts +51 -15
  243. package/src/router/handler-context.ts +71 -28
  244. package/src/router/instrument.ts +54 -2
  245. package/src/router/intercept-resolution.ts +141 -43
  246. package/src/router/loader-resolution.ts +115 -39
  247. package/src/router/match-api.ts +40 -26
  248. package/src/router/match-handlers.ts +5 -0
  249. package/src/router/match-middleware/background-revalidation.ts +161 -96
  250. package/src/router/match-middleware/cache-lookup.ts +110 -10
  251. package/src/router/match-middleware/cache-store.ts +48 -101
  252. package/src/router/middleware-types.ts +9 -6
  253. package/src/router/middleware.ts +75 -13
  254. package/src/router/prerender-match.ts +59 -19
  255. package/src/router/route-snapshot.ts +11 -1
  256. package/src/router/router-context.ts +2 -0
  257. package/src/router/router-interfaces.ts +25 -2
  258. package/src/router/router-options.ts +8 -5
  259. package/src/router/segment-resolution/fresh.ts +62 -21
  260. package/src/router/segment-resolution/helpers.ts +4 -2
  261. package/src/router/segment-resolution/loader-cache.ts +407 -76
  262. package/src/router/segment-resolution/loader-mask.ts +12 -22
  263. package/src/router/segment-resolution/loader-snapshot.ts +208 -56
  264. package/src/router/segment-resolution/mask-nested.ts +138 -44
  265. package/src/router/segment-resolution/revalidation.ts +61 -16
  266. package/src/router/segment-wrappers.ts +4 -0
  267. package/src/rsc/full-payload.ts +25 -1
  268. package/src/rsc/handler.ts +14 -2
  269. package/src/rsc/progressive-enhancement.ts +3 -2
  270. package/src/rsc/render-pipeline.ts +3 -0
  271. package/src/rsc/response-cache-serve.ts +9 -6
  272. package/src/rsc/response-route-handler.ts +11 -3
  273. package/src/rsc/rsc-rendering.ts +608 -225
  274. package/src/rsc/shell-build-manifest.ts +27 -9
  275. package/src/rsc/shell-capture-constants.ts +73 -6
  276. package/src/rsc/shell-capture.ts +1068 -296
  277. package/src/rsc/shell-serve.ts +316 -34
  278. package/src/rsc/ssr-setup.ts +14 -8
  279. package/src/rsc/types.ts +18 -0
  280. package/src/server/context.ts +300 -32
  281. package/src/server/cookie-store.ts +87 -61
  282. package/src/server/handle-store.ts +232 -7
  283. package/src/server/request-context.ts +297 -67
  284. package/src/ssr/index.tsx +136 -44
  285. package/src/ssr/ssr-root.tsx +9 -8
  286. package/src/static-handler.ts +4 -1
  287. package/src/testing/flight-runtime.d.ts +21 -0
  288. package/src/testing/flight.entry.ts +14 -0
  289. package/src/testing/generated-routes.ts +2 -2
  290. package/src/testing/internal/context.ts +1 -1
  291. package/src/testing/internal/flight-client-globals.ts +7 -5
  292. package/src/testing/render-route.tsx +85 -24
  293. package/src/testing/run-loader.ts +68 -6
  294. package/src/testing/serve-shell-request.ts +378 -0
  295. package/src/testing/shell-status.ts +34 -12
  296. package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
  297. package/src/testing/vitest.ts +62 -1
  298. package/src/theme/ThemeProvider.tsx +7 -6
  299. package/src/theme/ThemeScript.tsx +3 -3
  300. package/src/types/cache-types.ts +14 -10
  301. package/src/types/handler-context.ts +28 -1
  302. package/src/types/loader-types.ts +8 -6
  303. package/src/urls/pattern-types.ts +22 -18
  304. package/src/urls/response-types.ts +3 -23
  305. package/src/use-loader.tsx +12 -3
  306. package/src/vite/discovery/shell-prerender-phase.ts +9 -0
  307. package/src/vite/plugins/use-cache-transform.ts +118 -74
  308. package/src/vite/rango.ts +7 -3
  309. package/src/vite/router-discovery.ts +14 -4
@@ -1,12 +1,24 @@
1
1
  ---
2
2
  name: parallel
3
- description: Define parallel routes for multi-column layouts, sidebars, and modal slots in @rangojs/router. Use when a layout needs multiple independently-loading regions (e.g. a sidebar and main panel), or rendering more than one route segment at the same URL.
3
+ description: Define parallel routes (named slots) for multi-column layouts, sidebars, and UI-less metadata slots in @rangojs/router. Use when a layout needs multiple independently-loading regions (e.g. a sidebar and main panel), or rendering more than one route segment at the same URL.
4
4
  argument-hint: [@slot-name]
5
5
  ---
6
6
 
7
7
  # Parallel Routes
8
8
 
9
- Parallel routes render multiple components simultaneously in named slots.
9
+ `parallel({ "@slot": handler, ... }, use?)` renders extra components alongside
10
+ the page in named slots. The owning layout (or route) places each slot with
11
+ `<ParallelOutlet name="@slot" />`. Use it for sidebars, multi-column layouts,
12
+ UI-less metadata slots, and regions that load and stream independently.
13
+
14
+ Slot rules:
15
+
16
+ - Slot names start with `@` and must not contain `.` (a dot is rejected at
17
+ definition time).
18
+ - `parallel()` can't be nested inside another `parallel()`, and `intercept()`
19
+ can't be declared inside one.
20
+ - A slot value is a handler function, a ReactNode, a `Static()` definition, or
21
+ a slot descriptor `{ handler, use }` (see "Two scopes for explicit `use`").
10
22
 
11
23
  ## Not this skill if…
12
24
 
@@ -80,7 +92,7 @@ set by an outer handler or layout, revalidate that outer segment too, or
80
92
  have the parallel reload/guard the data itself.
81
93
 
82
94
  ```typescript
83
- path("/dashboard/:id", (ctx) => {
95
+ path("/dashboard/:id", async (ctx) => {
84
96
  const user = await getUser(ctx.params.id);
85
97
  ctx.set("user", user);
86
98
  return <DashboardPage user={user} />;
@@ -100,13 +112,18 @@ path("/dashboard/:id", (ctx) => {
100
112
 
101
113
  Parallel slot handlers can call `ctx.use(Meta)` or `ctx.use(Breadcrumbs)` to
102
114
  push handle data. (Loader bodies can too — see `/loader` → "Writing Handles
103
- from Loaders" — which often replaces the UI-less `@meta` slot below.) The data is associated with the **parent** layout or route
104
- segment, not the parallel segment itself. This is because parallels execute
105
- after their parent handler and inherit its segment scope.
115
+ from Loaders" — which often replaces the UI-less `@meta` slot below.)
116
+
117
+ A slot's pushes are stored under the slot's own segment (`<parent>.@slot`),
118
+ separate from the parent's pushes:
106
119
 
107
- This works well for document-level metadata — the handle data follows the
108
- parent's lifecycle (appears when the parent is mounted, removed when it
109
- unmounts).
120
+ - **Order:** they are collected immediately after the parent segment's pushes,
121
+ before any child route's. A route's `@meta` slot therefore comes after every
122
+ layout above it, and a layout-level slot comes before the routes it wraps.
123
+ - **Lifecycle:** they appear while the slot is mounted with its parent and are
124
+ removed when it unmounts.
125
+ - **Partial updates:** a slot-only revalidation replaces just the slot's
126
+ pushes; the parent's pushes stay intact.
110
127
 
111
128
  ```typescript
112
129
  parallel({
@@ -120,25 +137,29 @@ parallel({
120
137
  })
121
138
  ```
122
139
 
123
- Multiple parallels on the same parent can each push handle data — they all
124
- accumulate under the parent segment ID.
140
+ Multiple parallels on the same parent can each push handle data — each slot's
141
+ pushes accumulate in its own bucket.
125
142
 
126
143
  ### Pattern: `@meta` slot for per-route metadata overrides
127
144
 
128
145
  A dedicated `@meta` parallel slot lets routes define metadata separately from
129
146
  their handler logic. The layout sets defaults via a title template, and each
130
- route overrides via its own `@meta` slot. Since child segments push after
131
- parents and `collectMeta` uses last-wins deduplication, overrides work
132
- naturally.
147
+ route overrides via its own `@meta` slot. The route's slot pushes after the
148
+ layout, and `Meta` keeps the last value per key, so the override wins.
133
149
 
134
150
  > **Loader-derived metadata: push from the loader instead.** This slot's
135
- > `await ctx.use(ProductLoader)` routes the data through HANDLER consumption —
136
- > the baked lane under `cache()`/PPR — just to reach a `ctx.use(Meta)` call.
137
- > The loader can now push `Meta` itself (`/loader` → "Writing Handles from
138
- > Loaders"), keeping the data on the live lane; add
139
- > `loader(Def, { ssr: false })` when the meta must be in the SSR'd
140
- > head. Keep the `@meta` slot for metadata that is NOT loader-derived
141
- > (templates, static descriptions, structured data with independent inputs).
151
+ > `await ctx.use(ProductLoader)` routes the data through HANDLER consumption
152
+ > just to reach a `ctx.use(Meta)` call: under `cache()` the slot's output is
153
+ > stored with the segment, and on a `ppr` route it bakes into the shell and a
154
+ > HIT replays it without running the slot. The loader can push `Meta` itself
155
+ > (`/loader` → "Writing Handles from Loaders"). Without `ssr: false` the
156
+ > loader runs per request, but on a `ppr` route its push is not in the
157
+ > shell's `<head>` (it applies after hydration). Add
158
+ > `loader(Def, { ssr: false })` when the meta must be in the SSR'd head; on a
159
+ > `ppr` route that bakes the push into the shell, and a HIT serves it without
160
+ > running the loader when its value has no promises. Keep the `@meta` slot for
161
+ > metadata that is NOT loader-derived (templates, static descriptions,
162
+ > structured data with independent inputs).
142
163
 
143
164
  ```typescript
144
165
  // Layout sets defaults
@@ -183,13 +204,20 @@ parallel(
183
204
  "@sidebar": () => <CategorySidebar />,
184
205
  },
185
206
  () => [
186
- loader(CategoriesLoader),
207
+ // The loader keeps its own revalidation: by default it re-runs after
208
+ // every action. Scope it here to stop that.
209
+ loader(CategoriesLoader, () => [revalidate(() => false)]),
187
210
  loading(<SidebarSkeleton />),
188
- revalidate(() => false), // Never revalidate sidebar
211
+ revalidate(() => false), // the slot component renders once, then is kept
189
212
  ]
190
213
  )
191
214
  ```
192
215
 
216
+ A `revalidate()` in the slot's `use()` decides whether the **slot component**
217
+ re-renders. Each `loader()` inside it is revalidated separately, with its own
218
+ `revalidate()` items and the loader defaults (see `/loader` → "`revalidate()`
219
+ return shapes").
220
+
193
221
  ### Streaming Behavior
194
222
 
195
223
  Parallels with `loading()` are **independent streaming units**. They don't
@@ -225,6 +253,9 @@ parallel(
225
253
  Slot handlers can carry their own loader, loading, error/notFound boundaries, revalidation, and transition defaults via `.use`. The mount site then declares **just the slot names** — no per-call data wiring.
226
254
 
227
255
  ```typescript
256
+ import { loader, loading, revalidate, type Handler } from "@rangojs/router";
257
+ import { revalidateCartData } from "./revalidation-contracts";
258
+
228
259
  const CartSummary: Handler = async (ctx) => {
229
260
  const cart = await ctx.use(CartLoader);
230
261
  return <CartSummaryView cart={cart} />;
@@ -249,7 +280,7 @@ layout(<AccountLayout />, () => [
249
280
 
250
281
  A slot's `loading()` (whether from `handler.use` or explicit) makes that slot an independent streaming unit, exactly as in the **Streaming Behavior** section above.
251
282
 
252
- Under a shared artifact (`cache()`, `"use cache"`, a PPR shell), the server-side `await ctx.use(CartLoader)` above is the BAKED lane — the capture-time value (identity reads included) freezes into the artifact; consume the loader client-side (`useLoader` in a `"use client"` component) to keep the slot live per request. One rule, stated once: `/rango` → Invariants ("the consumption-lane rule").
283
+ Under a shared artifact (`cache()`, `"use cache"`, a PPR shell), the server-side `await ctx.use(CartLoader)` above is the BAKED lane — the capture-time value freezes into the artifact. Under `cache()`/`"use cache"` an identity read (`cookies()`, `headers()`) inside the loader bakes too; under a PPR shell it refuses the capture, so the route stays uncached. Consume the loader client-side (`useLoader` in a `"use client"` component, the slot's `loading()` as its boundary) to keep the slot live per request. One rule, stated once: `/rango` → Invariants ("the consumption-lane rule").
253
284
 
254
285
  The `parallel` mount site has the narrowest allow-list for `handler.use` items — slots cannot bring their own middleware or layout, only `revalidate`, `loader`, `loading`, `errorBoundary`, `notFoundBoundary`, and `transition`. See [skills/handler-use](../handler-use/SKILL.md) for the full table and merge rules.
255
286
 
@@ -291,8 +322,8 @@ When multiple `parallel()` calls define the same slot name, **the last
291
322
  definition wins**. Earlier definitions of that slot are removed. Other
292
323
  slots from the earlier call are preserved.
293
324
 
294
- This enables composition patterns where included routes override
295
- parent-defined slots:
325
+ This lets a mount site override one slot that a shared factory or a
326
+ handler's `.use` defined (see `/handler-use` → "Replacing a whole slot"):
296
327
 
297
328
  ```typescript
298
329
  layout(DashboardLayout, () => [
@@ -309,10 +340,11 @@ layout(DashboardLayout, () => [
309
340
  ])
310
341
  ```
311
342
 
312
- After resolution, the layout has two parallel entries:
313
-
314
- - `{ "@footer": () => <Footer /> }` (first call, `@sidebar` removed)
315
- - `{ "@sidebar": () => <CustomSidebar /> }` (second call, wins)
343
+ Each slot is stored as its own entry keyed by slot name, so a later
344
+ `parallel()` replaces only the keys it names. After resolution `@footer` comes
345
+ from the first call and `@sidebar` from the second — including that call's
346
+ `use()` items (loaders, loading, revalidate); the first call's items for
347
+ `@sidebar` are gone with it.
316
348
 
317
349
  ## Multiple Parallel Slots
318
350
 
@@ -359,18 +391,25 @@ parallel(
359
391
  },
360
392
  () => [
361
393
  loader(CartLoader),
362
- // Revalidate when cart actions occur
394
+ // Layout-level slot (kept after actions by default): also re-render after
395
+ // cart actions. Under a path() the slot re-renders after every action anyway.
363
396
  revalidate((ctx) => ctx.isAction(CartActions) || undefined),
364
397
  ]
365
398
  )
366
399
  ```
367
400
 
368
- Where the slot sits decides its action default. A parallel under a
369
- `path()` (or one of its orphan layouts) belongs to the route entry and
370
- revalidates together with it on every action — handler-set data stays
371
- consistent with no configuration. A parallel under a standalone
372
- `layout()` entry follows the parent-chain default instead: skipped on
373
- actions unless a `revalidate()` opts it in.
401
+ Where the slot sits decides its default (once it is on screen; the first
402
+ render always happens):
403
+
404
+ | Slot declared under | Navigation | Action |
405
+ | --------------------------------------- | ------------------------------------ | ---------- |
406
+ | a `path()` or one of its orphan layouts | re-renders when params/search change | re-renders |
407
+ | a standalone `layout()` | kept | kept |
408
+
409
+ A route-scoped slot belongs to the route entry, so handler-set data stays
410
+ consistent with no configuration. A layout-level slot follows the parent-chain
411
+ default: kept unless a `revalidate()` opts it in. The slot's loaders are
412
+ revalidated separately (see "Parallel Routes with Loaders").
374
413
 
375
414
  In either position, revalidating only the parallel does not re-run outer
376
415
  handlers/layouts. If the slot reads `ctx.get()` data established above
@@ -382,7 +421,7 @@ A `revalidate()` callback may return a hard `boolean`, a soft
382
421
  `undefined`) to defer to the next revalidator. See
383
422
  [loader/SKILL.md#revalidate-return-shapes](../loader/SKILL.md#revalidate-return-shapes)
384
423
  for the full contract — it's the same across `loader()`, `path()`,
385
- `layout()`, `parallel()`, and `intercept()`.
424
+ `layout()`, and `parallel()`.
386
425
 
387
426
  A route-scoped slot revalidates on ANY params or search change by default —
388
427
  including query-only navigations (`?tab=…`) its content doesn't depend on. If
@@ -396,14 +435,15 @@ either, and keeps the previous param's content on e.g. a product-to-product
396
435
  navigation.
397
436
 
398
437
  `revalidate()` decides whether to _re_-render a slot, never whether to render
399
- it the first time. A slot the browser has not rendered yet has nothing cached
400
- to keep showing, so returning `false` for it would just leave a hole. On that
401
- first render the decision is clamped to `true` and your callback's `false` is
402
- ignored; from the second visit onward it is honored and the browser keeps the
403
- copy it already has. This is what makes `revalidate(() => false)` mean "render
404
- once, then never refetch" rather than "sometimes never appear at all" — before
405
- the clamp, landing on a sibling route and navigating in left the slot blank
406
- until a full reload.
438
+ it the first time. A slot the browser has not rendered yet has nothing to keep
439
+ showing, so on that first render the decision is clamped to `true` and your
440
+ callback's `false` is ignored; from then on it is honored and the browser keeps
441
+ the copy it already has. So `revalidate(() => false)` means "render once, then
442
+ never re-render", never "don't appear".
443
+
444
+ Revalidate callbacks must be synchronous. A callback that returns a Promise is
445
+ ignored (the default decision is kept) and dev logs a warning — move async work
446
+ into a loader.
407
447
 
408
448
  ### Revalidation Contracts for Parallel Dependencies
409
449
 
@@ -412,9 +452,10 @@ the parallel consumer:
412
452
 
413
453
  ```typescript
414
454
  // revalidation-contracts.ts
455
+ import type { Revalidate } from "@rangojs/router";
415
456
  import * as CartActions from "./actions/cart";
416
457
 
417
- export const revalidateCartData = (ctx) =>
458
+ export const revalidateCartData: Revalidate = (ctx) =>
418
459
  ctx.isAction(CartActions) || undefined;
419
460
 
420
461
  layout(CartLayout, () => [