@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.
- package/README.md +254 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/navigation-transaction.d.ts +6 -0
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/scroll-restoration.d.ts +7 -0
- package/dist/types/cache/cache-exec-scope.d.ts +18 -1
- package/dist/types/cache/cache-scope.d.ts +34 -5
- package/dist/types/cache/handle-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +53 -13
- package/dist/types/cache/segment-codec.d.ts +10 -3
- package/dist/types/cache/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +15 -3
- package/dist/types/client.d.ts +4 -4
- package/dist/types/client.rsc.d.ts +4 -5
- package/dist/types/components/DefaultDocument.d.ts +5 -2
- package/dist/types/context-var.d.ts +6 -5
- package/dist/types/escape-script.d.ts +1 -1
- package/dist/types/handles/MetaTags.d.ts +5 -4
- package/dist/types/handles/Scripts.d.ts +8 -7
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/handles/script.d.ts +17 -16
- package/dist/types/href-client.d.ts +1 -1
- package/dist/types/html.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +3 -2
- package/dist/types/loader-store.d.ts +7 -4
- package/dist/types/reverse.d.ts +36 -4
- package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
- package/dist/types/route-definition/helpers-types.d.ts +33 -9
- package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
- package/dist/types/route-types.d.ts +1 -1
- package/dist/types/router/debug-manifest.d.ts +3 -2
- package/dist/types/router/error-handling.d.ts +20 -0
- package/dist/types/router/handler-context.d.ts +6 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +3 -2
- package/dist/types/router/router-context.d.ts +3 -1
- package/dist/types/router/router-interfaces.d.ts +24 -2
- package/dist/types/router/router-options.d.ts +8 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +37 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +3 -3
- package/dist/types/router/segment-resolution/mask-nested.d.ts +7 -7
- package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
- package/dist/types/router/segment-wrappers.d.ts +1 -1
- package/dist/types/rsc/response-route-handler.d.ts +1 -1
- package/dist/types/rsc/rsc-rendering.d.ts +1 -2
- package/dist/types/rsc/shell-capture-constants.d.ts +8 -0
- package/dist/types/rsc/shell-capture.d.ts +5 -4
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +10 -0
- package/dist/types/server/context.d.ts +33 -3
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +24 -21
- package/dist/types/ssr/index.d.ts +15 -0
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/generated-routes.d.ts +2 -2
- package/dist/types/testing/internal/context.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +32 -6
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
- package/dist/types/testing/vitest.d.ts +35 -1
- package/dist/types/theme/ThemeScript.d.ts +3 -3
- package/dist/types/types/cache-types.d.ts +14 -10
- package/dist/types/types/handler-context.d.ts +14 -0
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/response-types.d.ts +3 -12
- package/dist/types/use-loader.d.ts +12 -3
- package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
- package/dist/types/vite/rango.d.ts +1 -1
- package/dist/vite/index.js +1381 -1346
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +65 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +207 -97
- package/skills/caching/SKILL.md +295 -93
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +53 -31
- package/skills/cloudflare/SKILL.md +72 -20
- package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
- package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
- package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
- package/skills/comparison/SKILL.md +5 -0
- package/skills/comparison/references/framework-comparison.md +39 -33
- package/skills/composability/SKILL.md +38 -29
- package/skills/css/SKILL.md +10 -13
- package/skills/debug-manifest/SKILL.md +74 -31
- package/skills/defer-hydration/SKILL.md +2 -1
- package/skills/deployment-caching/SKILL.md +6 -1
- package/skills/document-cache/SKILL.md +92 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +53 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +31 -16
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +37 -15
- package/skills/hooks/urls.md +7 -5
- package/skills/host-router/SKILL.md +64 -12
- package/skills/i18n/SKILL.md +60 -34
- package/skills/intercept/SKILL.md +161 -26
- package/skills/layout/SKILL.md +106 -50
- package/skills/links/SKILL.md +51 -19
- package/skills/loader/SKILL.md +242 -106
- package/skills/middleware/SKILL.md +119 -23
- package/skills/migrate-nextjs/SKILL.md +100 -45
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +49 -38
- package/skills/migrate-react-router/component-migration.md +25 -6
- package/skills/migrate-react-router/data-and-actions.md +18 -5
- package/skills/migrate-react-router/route-mapping.md +12 -6
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +28 -14
- package/skills/parallel/SKILL.md +75 -39
- package/skills/ppr/SKILL.md +345 -150
- package/skills/prerender/SKILL.md +189 -100
- package/skills/rango/SKILL.md +65 -34
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +165 -60
- package/skills/router-setup/SKILL.md +418 -240
- package/skills/scripts/SKILL.md +31 -20
- package/skills/server-actions/SKILL.md +220 -59
- package/skills/shell-manifest/SKILL.md +12 -7
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +68 -32
- package/skills/testing/cache-prerender.md +55 -29
- package/skills/testing/client-components.md +59 -6
- package/skills/testing/e2e-parity.md +12 -2
- package/skills/testing/flight.md +19 -11
- package/skills/testing/handles.md +8 -5
- package/skills/testing/loader.md +8 -5
- package/skills/testing/middleware.md +8 -3
- package/skills/testing/predicates.md +120 -0
- package/skills/testing/render-handler.md +70 -22
- package/skills/testing/response-routes.md +11 -4
- package/skills/testing/reverse-and-types.md +20 -14
- package/skills/testing/server-actions.md +12 -3
- package/skills/testing/server-tree.md +9 -0
- package/skills/testing/setup.md +9 -5
- package/skills/theme/SKILL.md +106 -29
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +126 -47
- package/skills/typesafety/generated-files-and-cli.md +26 -14
- package/skills/typesafety/params-and-search.md +44 -19
- package/skills/typesafety/route-types.md +50 -14
- package/skills/use-cache/SKILL.md +200 -82
- package/skills/vercel/SKILL.md +21 -11
- package/skills/view-transitions/SKILL.md +41 -20
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/index.ts +2 -2
- package/src/browser/navigation-bridge.ts +3 -4
- package/src/browser/navigation-transaction.ts +22 -2
- package/src/browser/partial-update.ts +35 -25
- package/src/browser/react/ScrollRestoration.tsx +6 -6
- package/src/browser/rsc-router.tsx +1 -1
- package/src/browser/scroll-restoration.ts +53 -6
- package/src/cache/cache-exec-scope.ts +37 -4
- package/src/cache/cache-key-utils.ts +1 -6
- package/src/cache/cache-runtime.ts +230 -61
- package/src/cache/cache-scope.ts +76 -12
- package/src/cache/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +132 -28
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/tag-invalidation.ts +4 -3
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +16 -3
- package/src/client.rsc.tsx +10 -9
- package/src/client.tsx +5 -4
- package/src/components/DefaultDocument.tsx +7 -2
- package/src/context-var.ts +6 -5
- package/src/escape-script.ts +1 -1
- package/src/handles/MetaTags.tsx +13 -11
- package/src/handles/Scripts.tsx +11 -10
- package/src/handles/meta.ts +2 -2
- package/src/handles/script.ts +17 -16
- package/src/href-client.ts +1 -1
- package/src/html.ts +12 -0
- package/src/index.rsc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/loader-store.ts +7 -4
- package/src/prerender/build-shell-capture.ts +8 -11
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +117 -30
- package/src/route-definition/helpers-types.ts +33 -9
- package/src/route-definition/resolve-handler-use.ts +121 -6
- package/src/route-types.ts +0 -3
- package/src/router/debug-manifest.ts +10 -2
- package/src/router/error-handling.ts +51 -15
- package/src/router/handler-context.ts +34 -25
- package/src/router/intercept-resolution.ts +125 -39
- package/src/router/loader-resolution.ts +56 -11
- package/src/router/match-api.ts +12 -17
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +93 -10
- package/src/router/match-middleware/cache-store.ts +17 -90
- package/src/router/middleware-types.ts +3 -5
- package/src/router/middleware.ts +52 -9
- package/src/router/prerender-match.ts +53 -19
- package/src/router/route-snapshot.ts +11 -1
- package/src/router/router-context.ts +2 -0
- package/src/router/router-interfaces.ts +25 -2
- package/src/router/router-options.ts +8 -5
- package/src/router/segment-resolution/fresh.ts +44 -12
- package/src/router/segment-resolution/loader-cache.ts +205 -45
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +6 -6
- package/src/router/segment-resolution/mask-nested.ts +7 -7
- package/src/router/segment-resolution/revalidation.ts +34 -6
- package/src/router/segment-wrappers.ts +4 -0
- package/src/rsc/render-pipeline.ts +3 -0
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +8 -45
- package/src/rsc/shell-capture-constants.ts +13 -0
- package/src/rsc/shell-capture.ts +111 -90
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +11 -0
- package/src/server/context.ts +88 -11
- package/src/server/cookie-store.ts +5 -4
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +35 -30
- package/src/ssr/index.tsx +40 -7
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/generated-routes.ts +2 -2
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/internal/flight-client-globals.ts +7 -5
- package/src/testing/render-route.tsx +85 -24
- package/src/testing/run-loader.ts +5 -1
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +14 -0
- package/src/types/loader-types.ts +8 -6
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
|
@@ -44,10 +44,10 @@ and multi-region composition without replacing its routing or data model.
|
|
|
44
44
|
|
|
45
45
|
| Dimension | **Rango** | Next.js (App Router) | TanStack Start | Waku |
|
|
46
46
|
| ----------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
47
|
-
| Routing model | Code DSL (`urls()`/`include()`), named + `reverse()`
|
|
47
|
+
| Routing model | Code DSL (`urls()`/`include()`), named + `ctx.reverse()` | File-system convention | File or code, type-first | File-system (+ `createPages`) |
|
|
48
48
|
| Foundation | Vite + plugin-rsc | Turbopack (default in 16; Webpack opt-in) | [Vite or Rsbuild](https://tanstack.com/start/latest/docs/framework/react/overview) | Vite + plugin-rsc |
|
|
49
49
|
| RSC model | RSC-first | RSC-first | SSR/client-first; RSC opt-in (experimental) | RSC-first |
|
|
50
|
-
| Type-safe routes/params | Generated names, params, search, `reverse()`, response MIME
|
|
50
|
+
| Type-safe routes/params | Generated names, params, search, `ctx.reverse()`/`href()`, response MIME | Stable typed links; route-local param types | Best-in-class | Typed path params |
|
|
51
51
|
| Client render selection | Per-segment/loader `revalidate()` policy; typed action and result matching | Automatic segment reuse; refresh/invalidation APIs | Match/loader reload policy | Route refetch/reload |
|
|
52
52
|
| Slots and intercepts | Code-defined named slots with their own loaders/policy; conditional alternate soft-navigation compositions | File-system `@slot` + intercept conventions | Route masking, no parallel RSC slot graph | No equivalent route-slot graph |
|
|
53
53
|
| Caching | One segment store = runtime + build-time + `"use cache"`, tags, SWR | Cache Components plus distinct server/client cache lifecycles | Router loader cache + Query integration + HTTP/CDN policy | Minimal |
|
|
@@ -81,16 +81,16 @@ const router = createRouter().routes(({ path }) => [
|
|
|
81
81
|
As the application grows, each requirement adds one local primitive to that same
|
|
82
82
|
tree:
|
|
83
83
|
|
|
84
|
-
| When you need to… | Add…
|
|
85
|
-
| ------------------------------------------ |
|
|
86
|
-
| make navigation refactor-safe | a route `name` and `reverse()`
|
|
87
|
-
| split the application into modules | `urls()` and `include()`
|
|
88
|
-
| keep request data live beneath cached UI | `loader()`
|
|
89
|
-
| cache or prerender a shared shell | `cache()` or `Prerender()`
|
|
90
|
-
| choose what updates after an action | `revalidate()`
|
|
91
|
-
| render independent regions | `parallel()` and named slots
|
|
92
|
-
| open a route as a modal on soft navigation | `intercept()`
|
|
93
|
-
| inspect where request time went | `debugPerformance` or `telemetry`
|
|
84
|
+
| When you need to… | Add… |
|
|
85
|
+
| ------------------------------------------ | ---------------------------------- |
|
|
86
|
+
| make navigation refactor-safe | a route `name` and `ctx.reverse()` |
|
|
87
|
+
| split the application into modules | `urls()` and `include()` |
|
|
88
|
+
| keep request data live beneath cached UI | `loader()` |
|
|
89
|
+
| cache or prerender a shared shell | `cache()` or `Prerender()` |
|
|
90
|
+
| choose what updates after an action | `revalidate()` |
|
|
91
|
+
| render independent regions | `parallel()` and named slots |
|
|
92
|
+
| open a route as a modal on soft navigation | `intercept()` |
|
|
93
|
+
| inspect where request time went | `debugPerformance` or `telemetry` |
|
|
94
94
|
|
|
95
95
|
Nothing in the first route requires learning the last row. More importantly, the
|
|
96
96
|
last row does not force a migration to a second router, client data library, or
|
|
@@ -140,7 +140,11 @@ configure a node, so they can be imported or composed through small factories:
|
|
|
140
140
|
```tsx
|
|
141
141
|
const withAccountPolicy = () => [
|
|
142
142
|
middleware(requireUser),
|
|
143
|
-
|
|
143
|
+
// After an action, re-run only for account actions (a hard decision that ends
|
|
144
|
+
// the chain for later revalidators); on navigation, keep the default.
|
|
145
|
+
revalidate((ctx) =>
|
|
146
|
+
ctx.isAction() ? ctx.isAction(AccountActions) : undefined,
|
|
147
|
+
),
|
|
144
148
|
];
|
|
145
149
|
|
|
146
150
|
const accountPatterns = urls(({ path }) => [
|
|
@@ -208,7 +212,7 @@ you statically generate your top 1,000 products and serve the long tail
|
|
|
208
212
|
dynamically, from one definition. Next.js approximates this with
|
|
209
213
|
`generateStaticParams` + `dynamicParams`, but that is two separate knobs rather
|
|
210
214
|
than one decision point inside your handler. See
|
|
211
|
-
[prerender-api-design.md](
|
|
215
|
+
[prerender-api-design.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/prerender-api-design.md).
|
|
212
216
|
|
|
213
217
|
### Progressive enhancement is a tested contract
|
|
214
218
|
|
|
@@ -297,8 +301,8 @@ The router gives you guarantees most file-based routers leave implicit:
|
|
|
297
301
|
only; parallel siblings do not bleed into each other.
|
|
298
302
|
- **Dev/prod matching parity** — one trie is used in both dev and production, so
|
|
299
303
|
matching cannot drift between them. See
|
|
300
|
-
[matching-and-lazy-discovery.md](
|
|
301
|
-
and [execution-model.md](
|
|
304
|
+
[matching-and-lazy-discovery.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/internal/matching-and-lazy-discovery.md)
|
|
305
|
+
and [execution-model.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/internal/execution-model.md).
|
|
302
306
|
|
|
303
307
|
### Observability you do not wire yourself
|
|
304
308
|
|
|
@@ -311,7 +315,7 @@ Next.js also ships automatic OTel instrumentation; Rango's distinction is that t
|
|
|
311
315
|
same router-owned phase registry drives its traces, local waterfall, and
|
|
312
316
|
`Server-Timing` output. TanStack's strength is client/data devtools; Waku does not
|
|
313
317
|
ship an equivalent request-phase model. See
|
|
314
|
-
[telemetry.md](
|
|
318
|
+
[telemetry.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/telemetry.md).
|
|
315
319
|
|
|
316
320
|
### Deploy to Node, Cloudflare, and Vercel — and host many apps behind one entry
|
|
317
321
|
|
|
@@ -367,15 +371,17 @@ old-deployment request pinning.
|
|
|
367
371
|
### A shipped testing harness for server code
|
|
368
372
|
|
|
369
373
|
The `@rangojs/router/testing/*` entry points give you `runLoader`,
|
|
370
|
-
`runMiddleware`, `
|
|
371
|
-
rendering (`renderServerTree`,
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
374
|
+
`runMiddleware`, `runInRequestContext` (server actions), `dispatch` (base
|
|
375
|
+
`./testing`), `renderHandler` and real Flight rendering (`renderServerTree`,
|
|
376
|
+
`findClientBoundaries`, `findElements`) from `./testing/flight`, `renderRoute`
|
|
377
|
+
from `./testing/dom`, a vitest preset from `./testing/vitest`, and a Playwright
|
|
378
|
+
e2e harness with dev/prod parity helpers (`parityDescribe`, `expectParity`) from
|
|
379
|
+
`./testing/e2e`. You can unit-test a loader, a middleware, an action, or an RSC
|
|
380
|
+
handler in isolation. Next.js and Waku ship no official primitives for testing
|
|
381
|
+
server components/handlers; TanStack documents testing patterns (build your own
|
|
382
|
+
harness from `createRouter`/`createMemoryHistory`) but ships no testing package,
|
|
383
|
+
and nothing at the RSC-handler level. See the [testing skill](../../testing/SKILL.md)
|
|
384
|
+
and [testing.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/testing.md).
|
|
379
385
|
|
|
380
386
|
### Bundle discipline
|
|
381
387
|
|
|
@@ -386,7 +392,7 @@ the build if React's development bundle leaks into production. Treat those as
|
|
|
386
392
|
Rango baselines, not a normalized cross-framework benchmark: application shape,
|
|
387
393
|
React version, compiler output, and deployment transforms make headline bundle
|
|
388
394
|
comparisons unreliable. Waku deliberately targets a smaller surface. See
|
|
389
|
-
[client-chunking.md](
|
|
395
|
+
[client-chunking.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/client-chunking.md).
|
|
390
396
|
|
|
391
397
|
## Runtime mechanics in depth
|
|
392
398
|
|
|
@@ -495,7 +501,7 @@ layout(<ShopLayout />, () => [
|
|
|
495
501
|
A click from the list renders `ProductQuickView` into `@modal` and preserves the
|
|
496
502
|
list behind it. A direct visit or reload renders the canonical full page. Back
|
|
497
503
|
closes the modal and restores the preserved background. The intercept can own its
|
|
498
|
-
middleware, layout, loaders, loading
|
|
504
|
+
middleware, layout, loaders, loading state, and loader-level caching and
|
|
499
505
|
`revalidate()` rules; the `when` config selector (the 4th argument to
|
|
500
506
|
`intercept()`) can choose by navigation source.
|
|
501
507
|
|
|
@@ -521,12 +527,12 @@ There are two distinct things named "revalidate", and the split is deliberate:
|
|
|
521
527
|
downstream revalidators in the same decision chain. This is surgical
|
|
522
528
|
post-action control: "this widget re-renders only when `addToCart` ran; the
|
|
523
529
|
rest of the tree stays put."
|
|
524
|
-
See [is-action-api-design.md](
|
|
530
|
+
See [is-action-api-design.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/design/is-action-api-design.md).
|
|
525
531
|
2. **`revalidate: false` on `<Link>` / `navigate()`** — shallow navigation. When
|
|
526
532
|
the pathname is unchanged (a search or hash change), it updates the URL and all
|
|
527
533
|
location hooks but skips the server fetch and re-render entirely. For filters,
|
|
528
534
|
tabs, and pagination. See
|
|
529
|
-
[shallow-navigation.md](
|
|
535
|
+
[shallow-navigation.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/design/shallow-navigation.md).
|
|
530
536
|
|
|
531
537
|
Both are separate from `revalidateTag()`/`updateTag()`, which hard-purge tagged
|
|
532
538
|
cache entries. `updateTag()` is awaitable for read-your-own-writes;
|
|
@@ -776,7 +782,7 @@ contracts; they do not provide Rango's route-aware tainted-argument keying,
|
|
|
776
782
|
non-cacheable typed context variables, handle capture/replay, and loader escape
|
|
777
783
|
path as one system. TanStack Start and Waku leave this boundary primarily to
|
|
778
784
|
application architecture. See the [cache guide](../../cache-guide/SKILL.md) and
|
|
779
|
-
[`"use cache"` design](
|
|
785
|
+
[`"use cache"` design](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/use-cache-api-design.md).
|
|
780
786
|
|
|
781
787
|
## Performance diagnostics (`debugPerformance`)
|
|
782
788
|
|
|
@@ -809,7 +815,7 @@ Next.js has no equivalent flip-a-boolean per-request phase waterfall: its automa
|
|
|
809
815
|
OTel spans still need an exporter and trace viewer. TanStack's strength is
|
|
810
816
|
client/data devtools, a different axis from server request-phase diagnostics. Waku
|
|
811
817
|
does not ship an equivalent phase model. See
|
|
812
|
-
[telemetry.md](
|
|
818
|
+
[telemetry.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/telemetry.md).
|
|
813
819
|
|
|
814
820
|
## Coming from a specific framework
|
|
815
821
|
|
|
@@ -819,7 +825,7 @@ layers for a code-defined, refactor-safe router and one caching model. You gain
|
|
|
819
825
|
Cloudflare as a first-class target and phase spans for free. You give up the
|
|
820
826
|
largest ecosystem and talent pool in the React world — the real cost.
|
|
821
827
|
|
|
822
|
-
**From TanStack Start.** You get comparable type-safe routing (`reverse()`, typed
|
|
828
|
+
**From TanStack Start.** You get comparable type-safe routing (`ctx.reverse()`/`href()`, typed
|
|
823
829
|
params/search, even typed response MIME via `.json()`/`.text()`/`.image()`), plus a
|
|
824
830
|
true RSC-first model (zero-bundle server components, Flight) that TanStack's
|
|
825
831
|
client-first core added later and opt-in. You give up TanStack's best-in-class
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: composability
|
|
3
|
-
description: Reusable composition patterns with globally importable route helpers in @rangojs/router. Use when sharing loaders, middleware, or
|
|
3
|
+
description: Reusable composition patterns with globally importable route helpers and include() modules in @rangojs/router. Use when sharing loaders, middleware, or caching config across multiple route files, splitting routes into eager or code-split include() groups, or avoiding copy-pasting the same route setup everywhere.
|
|
4
4
|
argument-hint: "pattern-name"
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,6 +8,11 @@ argument-hint: "pattern-name"
|
|
|
8
8
|
|
|
9
9
|
Route helpers can be imported directly from `@rangojs/router` and used to build reusable composition factories. This enables sharing common route configurations across multiple routes and modules.
|
|
10
10
|
|
|
11
|
+
Use this skill when the same config (auth middleware, caching, loading and error
|
|
12
|
+
UI, revalidation) repeats across routes, when splitting a route tree into
|
|
13
|
+
modules with `include()`, or when sizing code-split route groups. To attach
|
|
14
|
+
defaults to one handler instead of a factory, see `/handler-use`.
|
|
15
|
+
|
|
11
16
|
## Globally Importable Helpers
|
|
12
17
|
|
|
13
18
|
These helpers can be imported and called outside the `urls()` callback parameter:
|
|
@@ -24,10 +29,11 @@ import {
|
|
|
24
29
|
intercept,
|
|
25
30
|
errorBoundary,
|
|
26
31
|
notFoundBoundary,
|
|
32
|
+
transition,
|
|
27
33
|
} from "@rangojs/router";
|
|
28
34
|
```
|
|
29
35
|
|
|
30
|
-
They work because they
|
|
36
|
+
They work because they read the active `urls()` builder from AsyncLocalStorage at call time, not import time. Calling one outside a running `urls()` builder throws a `DslContextError`.
|
|
31
37
|
|
|
32
38
|
## Why path() and include() Are Not Global
|
|
33
39
|
|
|
@@ -49,26 +55,25 @@ The globally importable helpers (`cache`, `middleware`, `loading`, etc.) are con
|
|
|
49
55
|
Define reusable factories that return arrays of use items:
|
|
50
56
|
|
|
51
57
|
```typescript
|
|
52
|
-
|
|
58
|
+
// route-config.tsx
|
|
59
|
+
import type { ReactNode } from "react";
|
|
60
|
+
import { cache, loading, errorBoundary, middleware } from "@rangojs/router";
|
|
61
|
+
import { authMiddleware, loggingMiddleware } from "./middleware";
|
|
53
62
|
|
|
54
63
|
// Shared caching configuration
|
|
55
|
-
const withCaching = () => [
|
|
56
|
-
cache({ ttl:
|
|
57
|
-
// Defer on navigation (|| undefined) so each route keeps its own param/search
|
|
58
|
-
// revalidation default; only force a re-run when an action ran.
|
|
59
|
-
revalidate(({ actionId }) => (actionId ? true : undefined)),
|
|
64
|
+
export const withCaching = () => [
|
|
65
|
+
cache({ ttl: 600 }), // seconds
|
|
60
66
|
];
|
|
61
67
|
|
|
62
68
|
// Shared loading and error handling
|
|
63
|
-
const withLoadingAndError = (skeleton: ReactNode) => [
|
|
69
|
+
export const withLoadingAndError = (skeleton: ReactNode) => [
|
|
64
70
|
loading(skeleton),
|
|
65
71
|
errorBoundary(() => <div>Something went wrong</div>),
|
|
66
72
|
];
|
|
67
73
|
|
|
68
|
-
// Shared
|
|
69
|
-
const withAuth = () => [
|
|
70
|
-
middleware(authMiddleware),
|
|
71
|
-
middleware(loggingMiddleware),
|
|
74
|
+
// Shared middleware (the array form registers both in order)
|
|
75
|
+
export const withAuth = () => [
|
|
76
|
+
middleware([authMiddleware, loggingMiddleware]),
|
|
72
77
|
];
|
|
73
78
|
```
|
|
74
79
|
|
|
@@ -97,7 +102,7 @@ const withAuth = () => [
|
|
|
97
102
|
|
|
98
103
|
## Using Factories in Routes
|
|
99
104
|
|
|
100
|
-
Place factory calls inside `path()`
|
|
105
|
+
Place factory calls inside any use callback (`path()`, `layout()`, `parallel()`, `intercept()`, `loader()`), as long as every item is valid there. The returned arrays are flattened automatically (up to 3 levels):
|
|
101
106
|
|
|
102
107
|
```typescript
|
|
103
108
|
import { urls } from "@rangojs/router";
|
|
@@ -126,13 +131,10 @@ Factories can be defined in shared modules and reused across separate `urls()` d
|
|
|
126
131
|
|
|
127
132
|
```typescript
|
|
128
133
|
// src/route-config.ts
|
|
129
|
-
import { cache,
|
|
134
|
+
import { cache, middleware } from "@rangojs/router";
|
|
130
135
|
import { authMiddleware } from "./middleware/auth";
|
|
131
136
|
|
|
132
|
-
export const withPublicDefaults = () => [
|
|
133
|
-
cache({ ttl: 300 }),
|
|
134
|
-
revalidate(({ actionId }) => (actionId ? true : undefined)),
|
|
135
|
-
];
|
|
137
|
+
export const withPublicDefaults = () => [cache({ ttl: 300 })];
|
|
136
138
|
|
|
137
139
|
export const withProtectedDefaults = () => [
|
|
138
140
|
middleware(authMiddleware),
|
|
@@ -195,7 +197,10 @@ urls(({ include }) => [
|
|
|
195
197
|
]);
|
|
196
198
|
```
|
|
197
199
|
|
|
198
|
-
The
|
|
200
|
+
The provider must resolve to a `urls()` value — either as the module's `default`
|
|
201
|
+
export (the usual form) or returned directly
|
|
202
|
+
(`() => import("./shop-patterns").then((m) => m.shopPatterns)`). Anything else
|
|
203
|
+
throws when the group is first loaded:
|
|
199
204
|
|
|
200
205
|
```typescript
|
|
201
206
|
// src/shop-patterns.ts
|
|
@@ -219,7 +224,7 @@ splitting a thin group buys little). Both match identically at runtime — only
|
|
|
219
224
|
module's runtime evaluation timing differs.
|
|
220
225
|
|
|
221
226
|
What you do NOT lose by splitting: build-time discovery `await`s the provider, so
|
|
222
|
-
`href()`, `reverse()`, generated route types, and prerender still see every route
|
|
227
|
+
`href()`, `ctx.reverse()`, generated route types, and prerender still see every route
|
|
223
228
|
in the group — including nested `include()`s inside the split module. Only the
|
|
224
229
|
module's runtime evaluation defers. `rango generate` resolves the `() => import()`
|
|
225
230
|
the same way, so a code-split group is still fully typed.
|
|
@@ -227,8 +232,10 @@ the same way, so a code-split group is still fully typed.
|
|
|
227
232
|
### Sizing async include groups (measured)
|
|
228
233
|
|
|
229
234
|
The first request into an async group pays that group's chunk import; every
|
|
230
|
-
request after that is flat.
|
|
231
|
-
26k
|
|
235
|
+
request after that is flat. One cold, single-run measurement right after
|
|
236
|
+
deploying a 26k-route app to Cloudflare Workers (2026-07, warm RTT floor
|
|
237
|
+
~23 ms, see `tests/cloudflare-stress-demo/BENCHMARK-2026-07-04-edge-26k.md`).
|
|
238
|
+
Treat the numbers as orders of magnitude:
|
|
232
239
|
|
|
233
240
|
| Group size | First-hit latency |
|
|
234
241
|
| -------------------------- | ------------------------------------ |
|
|
@@ -261,11 +268,12 @@ erases first-hit cost for the isolate entirely.
|
|
|
261
268
|
For typed factories, import the composition types:
|
|
262
269
|
|
|
263
270
|
```typescript
|
|
271
|
+
import { cache, loading, middleware } from "@rangojs/router";
|
|
264
272
|
import type { RouteUseItem, LayoutUseItem, UseItems } from "@rangojs/router";
|
|
265
273
|
|
|
266
274
|
// Factory for path() use callbacks
|
|
267
275
|
const withCaching = (): RouteUseItem[] => [
|
|
268
|
-
cache({ ttl:
|
|
276
|
+
cache({ ttl: 600 }),
|
|
269
277
|
];
|
|
270
278
|
|
|
271
279
|
// Factory for layout() use callbacks
|
|
@@ -281,12 +289,13 @@ const withEverything = (): UseItems<RouteUseItem> => [
|
|
|
281
289
|
```
|
|
282
290
|
|
|
283
291
|
- `RouteUseItem[]` -- flat array for `path()` use callbacks
|
|
284
|
-
- `LayoutUseItem[]` -- flat array for `layout()` use callbacks
|
|
285
|
-
- `UseItems<T>` -- allows nested arrays from composing factories together
|
|
292
|
+
- `LayoutUseItem[]` -- flat array for `layout()` use callbacks (every item type)
|
|
293
|
+
- `UseItems<T>` -- allows one level of nested arrays from composing factories together
|
|
294
|
+
- `HandlerUseItem` -- items a `handler.use` callback may return (see `/handler-use`)
|
|
286
295
|
|
|
287
296
|
## Rules
|
|
288
297
|
|
|
289
|
-
- Helpers execute lazily -- factory functions are defined anywhere, but only called
|
|
290
|
-
- Calling helpers outside a `urls()`
|
|
298
|
+
- Helpers execute lazily -- factory functions are defined anywhere, but only called while a `urls()` builder runs (inside a use callback)
|
|
299
|
+
- Calling helpers outside a `urls()` builder throws a `DslContextError`
|
|
291
300
|
- Nested arrays from factories are flattened automatically via `.flat(3)`
|
|
292
|
-
- `path()` and `include()`
|
|
301
|
+
- `path()` and `include()` are not exported as standalone helpers -- they define route structure and must remain visible in the `urls()` callback
|
package/skills/css/SKILL.md
CHANGED
|
@@ -7,9 +7,10 @@ argument-hint:
|
|
|
7
7
|
# CSS imports
|
|
8
8
|
|
|
9
9
|
Document/app CSS in Rango lives in the Document `<head>`, loaded with Vite's
|
|
10
|
-
`?url` import and a `precedence`-managed `<link rel="stylesheet">`. This page
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
`?url` import and a `precedence`-managed `<link rel="stylesheet">`. This page
|
|
11
|
+
explains that pattern, when a side-effect `import "./x.css"` is fine, and how CSS
|
|
12
|
+
behaves across host-router apps. `/tailwind` is the concrete Tailwind setup,
|
|
13
|
+
`/theme` is dark mode, `/fonts` is fonts.
|
|
13
14
|
|
|
14
15
|
## The pattern
|
|
15
16
|
|
|
@@ -18,7 +19,7 @@ is dark mode, `/fonts` is fonts.
|
|
|
18
19
|
"use client";
|
|
19
20
|
|
|
20
21
|
import type { ReactNode } from "react";
|
|
21
|
-
import {
|
|
22
|
+
import { Html } from "@rangojs/router/client";
|
|
22
23
|
import styles from "./index.css?url";
|
|
23
24
|
|
|
24
25
|
export function Document({ children }: { children: ReactNode }) {
|
|
@@ -27,7 +28,7 @@ export function Document({ children }: { children: ReactNode }) {
|
|
|
27
28
|
<head>
|
|
28
29
|
<link rel="preload" href={styles} as="style" precedence="default" />
|
|
29
30
|
<link rel="stylesheet" href={styles} precedence="default" />
|
|
30
|
-
<
|
|
31
|
+
<Html.Meta />
|
|
31
32
|
</head>
|
|
32
33
|
<body>{children}</body>
|
|
33
34
|
</html>
|
|
@@ -50,14 +51,10 @@ A client-side navigation that crosses an app boundary is a **full document load*
|
|
|
50
51
|
(the server returns `X-RSC-Reload` on an app switch — see `/host-router`), so the
|
|
51
52
|
target app's entire document — its stylesheets, theme, meta — is re-established
|
|
52
53
|
by the target app's own load. Each app owns its document; how one app renders a
|
|
53
|
-
stylesheet has no effect on another.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
to one hashed asset — could be silently dropped by React's by-`href` resource
|
|
58
|
-
dedup when the apps disagreed on `precedence` (one unmanaged, one managed). The
|
|
59
|
-
full reload removes that footgun entirely, which is the main reason cross-app
|
|
60
|
-
navigation is a hard boundary.)
|
|
54
|
+
stylesheet has no effect on another. In particular, apps may disagree on
|
|
55
|
+
`precedence` for a stylesheet they share by `href` (for example every app's
|
|
56
|
+
`@import "tailwindcss"` compiling to one hashed asset) without React's by-`href`
|
|
57
|
+
dedup dropping it.
|
|
61
58
|
|
|
62
59
|
## Side-effect imports vs `?url`
|
|
63
60
|
|
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: debug-manifest
|
|
3
|
-
description:
|
|
3
|
+
description: Inspect the router's route manifest (route/layout tree, shortCodes, parent links, loader/middleware flags) and diff it between versions. Use when routes aren't matching as expected, you need to see the generated route tree, or a path resolves to the wrong handler.
|
|
4
4
|
argument-hint:
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Debug Manifest
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Dump the route manifest the router builds from your `urls()` tree to verify
|
|
10
|
+
parent relationships, shortCodes, and which segments carry loaders, middleware,
|
|
11
|
+
error boundaries, parallel slots, or intercepts. This is a development aid; the
|
|
12
|
+
diff helpers below are internal and may change between releases.
|
|
10
13
|
|
|
11
|
-
## Programmatic
|
|
14
|
+
## Programmatic access
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
The router returned by `createRouter()` has an async `debugManifest()` method:
|
|
14
17
|
|
|
15
18
|
```typescript
|
|
16
19
|
import { router } from "./router.js";
|
|
17
20
|
|
|
18
|
-
//
|
|
21
|
+
// Development only
|
|
19
22
|
if (process.env.NODE_ENV !== "production") {
|
|
20
23
|
const manifest = await router.debugManifest();
|
|
21
24
|
console.log(JSON.stringify(manifest, null, 2));
|
|
22
25
|
}
|
|
23
26
|
```
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
Run it on the server side (for example from a dev-only route handler), since
|
|
29
|
+
it evaluates your `urls()` tree. The result type is `SerializedManifest`
|
|
30
|
+
(`import type { SerializedManifest } from "@rangojs/router"`).
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
### `include()` groups are not expanded
|
|
33
|
+
|
|
34
|
+
`include()` groups are lazy: the router evaluates one on the first request that
|
|
35
|
+
matches its prefix. `debugManifest()` does not evaluate them. It lists the
|
|
36
|
+
routes and layouts declared directly in each `.routes()` / `urls` mount, so the
|
|
37
|
+
routes inside an included group are missing from `routes` and `totalRoutes`.
|
|
38
|
+
For their names and patterns, read the generated `*.named-routes.gen.ts` file,
|
|
39
|
+
which lists every route including those in `include()` groups.
|
|
40
|
+
|
|
41
|
+
## Manifest structure
|
|
42
|
+
|
|
43
|
+
`debugManifest()` returns `{ routes, layouts, totalRoutes, totalLayouts }`.
|
|
44
|
+
Every entry has the same fields; `pattern` is present on routes only. Cache
|
|
45
|
+
boundaries are listed under `layouts` with `type: "cache"`. Shape (values are
|
|
46
|
+
illustrative):
|
|
28
47
|
|
|
29
48
|
```json
|
|
30
49
|
{
|
|
@@ -47,7 +66,12 @@ The programmatic `router.debugManifest()` call returns `{ routes, layouts, total
|
|
|
47
66
|
"id": "debug.M0.$root",
|
|
48
67
|
"shortCode": "M0L0",
|
|
49
68
|
"type": "layout",
|
|
50
|
-
"parentShortCode": null
|
|
69
|
+
"parentShortCode": null,
|
|
70
|
+
"hasLoader": false,
|
|
71
|
+
"hasMiddleware": false,
|
|
72
|
+
"hasErrorBoundary": false,
|
|
73
|
+
"parallelCount": 0,
|
|
74
|
+
"interceptCount": 0
|
|
51
75
|
}
|
|
52
76
|
},
|
|
53
77
|
"totalRoutes": 45,
|
|
@@ -55,30 +79,39 @@ The programmatic `router.debugManifest()` call returns `{ routes, layouts, total
|
|
|
55
79
|
}
|
|
56
80
|
```
|
|
57
81
|
|
|
58
|
-
## ShortCode
|
|
82
|
+
## ShortCode format
|
|
59
83
|
|
|
60
|
-
|
|
61
|
-
| ------ | ---------------------------------------- |
|
|
62
|
-
| **M** | Mount index (multiple `.routes()` calls) |
|
|
63
|
-
| **L** | Layout |
|
|
64
|
-
| **C** | Cache boundary |
|
|
65
|
-
| **R** | Route |
|
|
66
|
-
| **P** | Parallel slot |
|
|
84
|
+
A shortCode is the parent's shortCode plus one segment per level:
|
|
67
85
|
|
|
68
|
-
|
|
86
|
+
| Prefix | Meaning |
|
|
87
|
+
| ------ | ----------------------------------------------------------------------- |
|
|
88
|
+
| **M** | Mount index (`.routes()` / `urls` registrations and `include()` mounts) |
|
|
89
|
+
| **L** | Layout (the router adds a root layout, `M<n>L0`) |
|
|
90
|
+
| **C** | Cache boundary |
|
|
91
|
+
| **R** | Route |
|
|
92
|
+
| **P** | Parallel slot |
|
|
93
|
+
| **I** | `include()` scope token, so included routes never collide with siblings |
|
|
69
94
|
|
|
70
|
-
|
|
95
|
+
Example: `M0L0L1C0R0` = Mount 0 → Root Layout → Nested Layout → Cache → Route.
|
|
96
|
+
At runtime, a route from an `include()` placed under the root layout gets a
|
|
97
|
+
shortCode such as `M0L0I0R0`.
|
|
71
98
|
|
|
72
|
-
|
|
73
|
-
2. **Correct hierarchy**: ShortCode should reflect nesting (e.g., `M0L0R0` not `M0R0`)
|
|
74
|
-
3. **Loaders attached**: Check `hasLoader: true` for routes with data requirements
|
|
75
|
-
4. **Intercepts registered**: `interceptCount > 0` for modal/overlay patterns
|
|
99
|
+
## Debugging checklist
|
|
76
100
|
|
|
77
|
-
|
|
101
|
+
1. **Correct hierarchy**: the shortCode reflects nesting (`M0L0L1R0` for a
|
|
102
|
+
route in a nested layout, not `M0L0R0`).
|
|
103
|
+
2. **Parents**: only a mount's root layout (`M<n>L0`) has
|
|
104
|
+
`parentShortCode: null`.
|
|
105
|
+
3. **Loaders attached**: `hasLoader: true` on routes/layouts that declare
|
|
106
|
+
`loader()`.
|
|
107
|
+
4. **Intercepts registered**: `interceptCount > 0` where you declared
|
|
108
|
+
`intercept()` for modal/overlay patterns.
|
|
109
|
+
5. **Slots**: `parallelCount` matches the number of `parallel()` slots.
|
|
110
|
+
|
|
111
|
+
## Comparing manifests
|
|
78
112
|
|
|
79
113
|
```typescript
|
|
80
114
|
import {
|
|
81
|
-
serializeManifest,
|
|
82
115
|
compareManifests,
|
|
83
116
|
formatManifestDiff,
|
|
84
117
|
} from "@rangojs/router/__internal";
|
|
@@ -91,16 +124,26 @@ const diff = compareManifests(oldManifest, newManifest);
|
|
|
91
124
|
console.log(formatManifestDiff(diff));
|
|
92
125
|
```
|
|
93
126
|
|
|
94
|
-
|
|
127
|
+
`compareManifests` reports added, removed, and changed routes and layouts
|
|
128
|
+
(field-level: `old` / `new` per changed field).
|
|
129
|
+
|
|
130
|
+
## Common issues
|
|
95
131
|
|
|
96
|
-
###
|
|
132
|
+
### A route sits under the wrong layout
|
|
97
133
|
|
|
98
|
-
|
|
134
|
+
Check that the `layout()` call wraps the route in its `use` callback
|
|
135
|
+
(`layout(<Shell />, () => [path(...)])`). A `path()` listed next to a
|
|
136
|
+
`layout()` is a sibling, not a child.
|
|
99
137
|
|
|
100
|
-
###
|
|
138
|
+
### Unexpected mount index
|
|
101
139
|
|
|
102
|
-
|
|
140
|
+
Each `.routes()` call (and the `urls` option) registers a separate mount (M0,
|
|
141
|
+
M1, ...), and each `include()` also takes a mount index. Routes registered by
|
|
142
|
+
separate `.routes()` calls do not share layouts or middleware; compose modules
|
|
143
|
+
with `include()` inside one `urls()` tree when they should (`/composability`).
|
|
103
144
|
|
|
104
|
-
###
|
|
145
|
+
### Missing flags
|
|
105
146
|
|
|
106
|
-
|
|
147
|
+
`hasMiddleware` / `hasLoader` / `hasErrorBoundary` are per entry. Middleware or
|
|
148
|
+
a loader declared on a parent layout shows on that layout, not on each child
|
|
149
|
+
route.
|
|
@@ -32,7 +32,8 @@ hydration cost lives under the boundary (see "Verifying and measuring").
|
|
|
32
32
|
|
|
33
33
|
## The recipe
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
About 50 lines of plain React, no rango imports. Copy it into a client module in
|
|
36
|
+
your app:
|
|
36
37
|
|
|
37
38
|
```tsx
|
|
38
39
|
"use client";
|
|
@@ -10,6 +10,9 @@ Start with one question: **does the request reach Rango before shared bytes are
|
|
|
10
10
|
served?** That boundary decides whether middleware runs, whether loaders stay
|
|
11
11
|
live, and which invalidation system owns the result.
|
|
12
12
|
|
|
13
|
+
This skill is about where each cache sits relative to your deployment platform.
|
|
14
|
+
To choose between the in-app layers themselves, start at `/cache-guide`.
|
|
15
|
+
|
|
13
16
|
## The execution matrix
|
|
14
17
|
|
|
15
18
|
| Mechanism | Stored artifact | Function/worker runs on a hit? | Rango middleware on a hit? | What stays live? |
|
|
@@ -173,4 +176,6 @@ provably public and shared.
|
|
|
173
176
|
- `/document-cache` — store-backed complete-response middleware
|
|
174
177
|
- `/vercel` — Vercel Build Output preset and Runtime Cache wiring
|
|
175
178
|
- `/cloudflare` — Worker deployment and bindings
|
|
176
|
-
- `/cache-guide` —
|
|
179
|
+
- `/cache-guide` — choosing between the in-app cache layers
|
|
180
|
+
- `/caching` — `cache()` segments, stores, and tag invalidation
|
|
181
|
+
- `/use-cache` — function-level `"use cache"`
|