@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.
- package/README.md +255 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -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-key-utils.d.ts +17 -2
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +132 -19
- package/dist/types/cache/cache-tag.d.ts +92 -7
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
- package/dist/types/cache/handle-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +54 -13
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/segment-codec.d.ts +10 -3
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +110 -21
- package/dist/types/cache/tag-invalidation.d.ts +30 -3
- package/dist/types/cache/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +220 -44
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- 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/prerender/build-shell-capture.d.ts +11 -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/instrument.d.ts +8 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +9 -3
- 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 +66 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
- package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
- 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/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.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-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
- package/dist/types/rsc/shell-capture.d.ts +151 -19
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +17 -0
- package/dist/types/server/context.d.ts +139 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +182 -53
- package/dist/types/ssr/index.d.ts +24 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- 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/run-loader.d.ts +18 -3
- package/dist/types/testing/serve-shell-request.d.ts +132 -0
- package/dist/types/testing/shell-status.d.ts +23 -10
- 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 +28 -1
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/pattern-types.d.ts +22 -18
- 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/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1420 -1348
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +75 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +306 -124
- package/skills/caching/SKILL.md +530 -98
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +65 -38
- package/skills/cloudflare/SKILL.md +87 -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 +75 -57
- 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 +52 -31
- package/skills/deployment-caching/SKILL.md +11 -3
- package/skills/document-cache/SKILL.md +98 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +55 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +34 -17
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +39 -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 +386 -128
- package/skills/middleware/SKILL.md +129 -26
- package/skills/migrate-nextjs/SKILL.md +156 -70
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +51 -39
- package/skills/migrate-react-router/component-migration.md +28 -7
- package/skills/migrate-react-router/data-and-actions.md +21 -6
- package/skills/migrate-react-router/route-mapping.md +19 -10
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +92 -14
- package/skills/parallel/SKILL.md +88 -47
- package/skills/ppr/SKILL.md +866 -379
- package/skills/prerender/SKILL.md +210 -108
- package/skills/rango/SKILL.md +115 -65
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +170 -60
- package/skills/router-setup/SKILL.md +420 -240
- package/skills/scripts/SKILL.md +39 -21
- package/skills/server-actions/SKILL.md +231 -59
- package/skills/shell-manifest/SKILL.md +28 -14
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +73 -33
- package/skills/testing/cache-prerender.md +60 -33
- 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 +26 -6
- 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/serve-shell-request.md +188 -0
- 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 +155 -30
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +137 -50
- 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 +224 -83
- package/skills/vercel/SKILL.md +35 -13
- package/skills/view-transitions/SKILL.md +42 -21
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- 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 +6 -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 +23 -8
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +264 -68
- package/src/cache/cache-scope.ts +419 -43
- package/src/cache/cache-tag.ts +375 -11
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +990 -348
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
- package/src/cache/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +133 -28
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +108 -22
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +225 -44
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- 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 +73 -22
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +121 -33
- 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 +71 -28
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +141 -43
- package/src/router/loader-resolution.ts +115 -39
- package/src/router/match-api.ts +40 -26
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +110 -10
- package/src/router/match-middleware/cache-store.ts +48 -101
- package/src/router/middleware-types.ts +9 -6
- package/src/router/middleware.ts +75 -13
- package/src/router/prerender-match.ts +59 -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 +62 -21
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +407 -76
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +208 -56
- package/src/router/segment-resolution/mask-nested.ts +138 -44
- package/src/router/segment-resolution/revalidation.ts +61 -16
- package/src/router/segment-wrappers.ts +4 -0
- package/src/rsc/full-payload.ts +25 -1
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/render-pipeline.ts +3 -0
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +608 -225
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +73 -6
- package/src/rsc/shell-capture.ts +1068 -296
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +18 -0
- package/src/server/context.ts +300 -32
- package/src/server/cookie-store.ts +87 -61
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +297 -67
- package/src/ssr/index.tsx +136 -44
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +14 -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 +68 -6
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +28 -1
- package/src/types/loader-types.ts +8 -6
- package/src/urls/pattern-types.ts +22 -18
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
- package/src/vite/router-discovery.ts +14 -4
|
@@ -1,42 +1,98 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cache-guide
|
|
3
|
-
description:
|
|
3
|
+
description: Decision guide for Rango's cache layers — "use cache", loader cache(), segment cache(), Prerender/Static, the ppr HTML shell, and document/CDN caching — with the cache() vs "use cache" comparison (keys, guards, SWR, nesting). Use when unsure which caching mechanism fits a problem, comparing layers, or asking "which cache API should I use".
|
|
4
4
|
argument-hint:
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
`
|
|
12
|
-
|
|
7
|
+
# Choosing a cache layer
|
|
8
|
+
|
|
9
|
+
Start here when you are not sure which cache to use. The first section maps
|
|
10
|
+
every layer; the rest of the page compares the two you will reach for most,
|
|
11
|
+
the `cache()` DSL and the `"use cache"` directive.
|
|
12
|
+
|
|
13
|
+
## The layers at a glance
|
|
14
|
+
|
|
15
|
+
Each layer stores a more "cooked" artifact than the one above it, and keeps less
|
|
16
|
+
of the request live on a hit. The runtime layers share the app-level store
|
|
17
|
+
(`createRouter({ cache })`) and one tag system (`cacheTag` / `updateTag` /
|
|
18
|
+
`revalidateTag`); `Prerender()`/`Static()` output is built into the server
|
|
19
|
+
bundle instead.
|
|
20
|
+
|
|
21
|
+
| Layer | Declared with | Stores | Still runs on a hit | Skill |
|
|
22
|
+
| -------------------- | ---------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------ | --------------------- |
|
|
23
|
+
| Function / component | `"use cache"` | one function's return value | everything around the call | `/use-cache` |
|
|
24
|
+
| Loader data | `loader(L, () => [cache({...})])` | one loader's result | other loaders, handlers, rendering | `/caching`, `/loader` |
|
|
25
|
+
| Segments, runtime | `cache({...}, () => [...])` | rendered Flight segments of a subtree | middleware, segments above it, loaders, HTML render | `/caching` |
|
|
26
|
+
| Segments, build time | `Prerender()` / `Static()` | Flight segments rendered at build | middleware, loaders, HTML render | `/prerender` |
|
|
27
|
+
| HTML shell | `ppr` path option | HTML prelude + React postponed state + the handler layer | middleware and live loaders (no handler); holes resume | `/ppr` |
|
|
28
|
+
| Whole response (app) | `createDocumentCacheMiddleware()` + `s-maxage` | final response in the app store | middleware above it; nothing below | `/document-cache` |
|
|
29
|
+
| Whole response (CDN) | `Cache-Control: s-maxage` read by the platform | final response outside the app | nothing — the app is not invoked | `/deployment-caching` |
|
|
30
|
+
|
|
31
|
+
Quick rules:
|
|
32
|
+
|
|
33
|
+
- Expensive **data** used in several places → `"use cache"` on the fetch.
|
|
34
|
+
- Expensive **rendering** of a route subtree → `cache()`.
|
|
35
|
+
- Content fixed at **build time** (known params, files on disk) → `Prerender()`.
|
|
36
|
+
- **Instant first byte** of HTML while parts stay per-request → `ppr`.
|
|
37
|
+
- **Whole response** public and identical for everyone → `/document-cache` or
|
|
38
|
+
CDN caching (`/deployment-caching`).
|
|
39
|
+
- A value that must be fresh on **every** request → a loader (never cached
|
|
40
|
+
unless you opt in), read with `useLoader()`; on a `ppr` route, keep it off
|
|
41
|
+
`ssr: false`. A handler inside a `cache()` or `ppr` boundary that awaits
|
|
42
|
+
`ctx.use(Loader)` renders the value it got on the miss, and every hit
|
|
43
|
+
replays that output.
|
|
44
|
+
|
|
45
|
+
The layers compose: `"use cache"` inside a `cache()` subtree, a cached loader
|
|
46
|
+
under a `cache()` boundary, `cache()` segments replayed inside a `ppr` shell
|
|
47
|
+
capture, or `Prerender()` + `ppr` for a shell baked at build time.
|
|
48
|
+
|
|
49
|
+
## cache() vs "use cache"
|
|
50
|
+
|
|
51
|
+
Both mechanisms use the same app-level store, and both can be tagged
|
|
52
|
+
(`cache({ tags })`; profile `tags` or runtime `cacheTag()` for `"use cache"`) and
|
|
53
|
+
invalidated with `updateTag`/`revalidateTag` — see "Two axes" below. Named
|
|
54
|
+
cache profiles apply to `"use cache"` only. They differ in scope, cache key,
|
|
55
|
+
execution model, and runtime control.
|
|
13
56
|
|
|
14
57
|
## Two axes — do not conflate
|
|
15
58
|
|
|
16
|
-
Everything on this page is **
|
|
17
|
-
|
|
59
|
+
Everything on this page is **stored-value freshness** — _is a cached value
|
|
60
|
+
still good?_ There is a second, orthogonal concern it is easy to mistake for
|
|
18
61
|
caching:
|
|
19
62
|
|
|
20
|
-
1. **Stored-value freshness** — _is a cached value still good?_
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`cacheTag(...tags)` — inside `"use cache"` it tags that entry; called during
|
|
24
|
-
request render outside `"use cache"` it tags the
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
63
|
+
1. **Stored-value freshness** — _is a cached value still good?_ → `"use cache"`
|
|
64
|
+
(fn/component), `cache()` (segment), loader `cache()` (loader data). Entries
|
|
65
|
+
expire by **TTL/SWR** and can be tagged (`cache({ tags })` or runtime
|
|
66
|
+
`cacheTag(...tags)` — inside `"use cache"` it tags that entry; called during
|
|
67
|
+
a request render outside `"use cache"` it tags what the render stores: the
|
|
68
|
+
enclosing route `cache()` entry, a cached loader's own entry when called in
|
|
69
|
+
its body, and the `ppr` shell or document-cache entry built from them; on a
|
|
70
|
+
`ppr` route it tags the shell only when shell material calls it). All
|
|
71
|
+
built-in stores (`MemorySegmentCacheStore`, `CFCacheStore`,
|
|
72
|
+
`VercelCacheStore`) index by tag; invalidate on demand with
|
|
73
|
+
`updateTag(...tags)` (awaitable) or `revalidateTag(...tags)` (background,
|
|
74
|
+
non-blocking). Both evict rather than mark stale, and the request that calls
|
|
75
|
+
either reads its own writes; `updateTag()` also waits for the durable write,
|
|
76
|
+
`revalidateTag()` leaves it in the background. `CFCacheStore` and
|
|
77
|
+
`VercelCacheStore` serve PPR shell reads through per-isolate memos: the
|
|
78
|
+
invalidating user skips them via the fresh-reads cookie, other users see the
|
|
79
|
+
invalidation once the memo refreshes and, on `CFCacheStore` with KV, once KV
|
|
80
|
+
propagates the marker to their colo (`/caching` → "Tag-Based Invalidation"
|
|
81
|
+
and "The fresh-reads cookie").
|
|
29
82
|
2. **Client-update selection** — _should this segment re-run and stream to the
|
|
30
83
|
client on this navigation/action?_
|
|
31
84
|
→ `revalidate()`. Covered in `/loader` and `/route`, **not here**.
|
|
32
85
|
|
|
33
86
|
They are orthogonal and compose: a segment selected by `revalidate()` still
|
|
34
|
-
consults its cache (hit → no recompute)
|
|
87
|
+
consults its cache (hit → no recompute), except that a server action's
|
|
88
|
+
revalidation render skips the route `cache()` lookup and re-renders those
|
|
89
|
+
segments fresh (a loader's own `cache()` and `"use cache"` are still read); a
|
|
90
|
+
cache bust does **not** force a client
|
|
35
91
|
update, and `revalidate()` never reads, writes, or expires a cached value. If you
|
|
36
92
|
know React Router, `revalidate()` is `shouldRevalidate`, not `Cache-Control`. See
|
|
37
93
|
`/rango` → "Coming from another framework" for the cross-framework mapping.
|
|
38
94
|
|
|
39
|
-
## Fast choice
|
|
95
|
+
## Fast choice: cache() or "use cache"
|
|
40
96
|
|
|
41
97
|
Read this first; use the rest of the page when the choice has edge cases.
|
|
42
98
|
|
|
@@ -62,20 +118,35 @@ protection the framework already gives you (or assume one it deliberately
|
|
|
62
118
|
doesn't).
|
|
63
119
|
|
|
64
120
|
There are two guard models to keep separate. Both block response side effects
|
|
65
|
-
(`ctx.
|
|
121
|
+
(`ctx.headers.set()`, cookie writes) that would be lost on a hit; they differ in what
|
|
66
122
|
else they allow:
|
|
67
123
|
|
|
68
124
|
- **`cache()` boundary guard** (route-level) — fires while the handler runs on a
|
|
69
125
|
miss. `cookies()` and `headers()` throw (request-scoped data would be baked into
|
|
70
126
|
the shared cached shell), `ctx.get(nonCacheableVar)` throws (a tainted value
|
|
71
|
-
would be baked in), and response side effects (`ctx.
|
|
72
|
-
`
|
|
127
|
+
would be baked in), and response side effects (`ctx.headers.set()`,
|
|
128
|
+
`setCookie()`, `setStatus()`, `onResponse()`) throw. `ctx.set()` of a cacheable var is
|
|
73
129
|
**allowed** — children are cached too and can read it. **Loaders are exempt**
|
|
74
|
-
(
|
|
130
|
+
(a route `cache()` does not store their values) — read request data inside a
|
|
131
|
+
loader.
|
|
132
|
+
- **Loader `cache()` guard** (a loader bound with its own `cache()`) — its value
|
|
133
|
+
is stored under a key that names no user, so a miss whose body read
|
|
134
|
+
`cookies()`, `headers()`, the theme or a non-cacheable `ctx.get()` fails
|
|
135
|
+
unless the binding has a `key()` or its store a `keyGenerator`. Either one
|
|
136
|
+
switches the check off, so put what the body reads in it (`/loader` →
|
|
137
|
+
"Cache Key").
|
|
75
138
|
- **`"use cache"` exec-guard** (function-level) — the same request-scoped APIs
|
|
76
139
|
throw inside the cached function (`cookies()`, `headers()`, `ctx.set()`,
|
|
77
|
-
`ctx.
|
|
78
|
-
the cache key.
|
|
140
|
+
`ctx.headers.set()` and other response writes); additionally, tainted
|
|
141
|
+
`ctx`/`env`/`req` args are excluded from the cache key. The guard runs only on
|
|
142
|
+
the cached path: with no item-capable store configured the function runs
|
|
143
|
+
uncached and nothing throws.
|
|
144
|
+
|
|
145
|
+
The `ppr` shell capture has its own, stricter guard: `cookies()`, `headers()`, a
|
|
146
|
+
theme read, a `{ cache: false }` variable read, and `ctx.dynamic()` refuse the
|
|
147
|
+
capture anywhere it waits — handlers, promises they pass or push, async server
|
|
148
|
+
components, `ssr: false` loaders, and loaders a handler awaits (no loader
|
|
149
|
+
exemption there). The route then serves uncached; see `/ppr`.
|
|
79
150
|
|
|
80
151
|
### Cross-deploy safety: version-segmented store keys
|
|
81
152
|
|
|
@@ -94,7 +165,9 @@ deliberately: accept it (correctness over hit-rate), or split the policy — let
|
|
|
94
165
|
the render/edge cache auto-version while a separate data store gets a stable
|
|
95
166
|
`version` so its entries survive deploys. (Per-process stores like
|
|
96
167
|
`MemorySegmentCacheStore` are cold on every restart anyway; this matters for
|
|
97
|
-
persistent stores.)
|
|
168
|
+
persistent stores.) `VercelCacheStore` does not version automatically: pass a
|
|
169
|
+
per-deploy `getCache({ namespace })` or its `version` option (see `/vercel`).
|
|
170
|
+
See `/caching` for store setup.
|
|
98
171
|
|
|
99
172
|
### Client cache: forward/back is mutation-aware
|
|
100
173
|
|
|
@@ -110,9 +183,10 @@ solved on the partial-render axis.
|
|
|
110
183
|
|
|
111
184
|
`createVar({ cache: false })` (or a `ctx.set(var, v, { cache: false })` write)
|
|
112
185
|
taints a value as request-scoped; reading it **directly** with `ctx.get()` inside
|
|
113
|
-
a `cache()` boundary throws — the guard against the
|
|
114
|
-
data to user B" bug. The guarantee is precise and
|
|
115
|
-
"Context Variable Cache Safety" below for exactly
|
|
186
|
+
a `cache()` boundary or a `"use cache"` function throws — the guard against the
|
|
187
|
+
catastrophic "serve user A's data to user B" bug. The guarantee is precise and
|
|
188
|
+
intentionally narrow — see "Context Variable Cache Safety" below for exactly
|
|
189
|
+
what it does and does not catch.
|
|
116
190
|
|
|
117
191
|
## Stale-while-revalidate
|
|
118
192
|
|
|
@@ -139,40 +213,58 @@ recompute for a merely-aging entry.
|
|
|
139
213
|
SWR softens normal TTL expiry, **not** a cross-deploy cold cache — a new build
|
|
140
214
|
has no stale entry to serve (see version-segmented store keys above).
|
|
141
215
|
|
|
142
|
-
Store support is layer-specific. `CFCacheStore`
|
|
143
|
-
document/response,
|
|
144
|
-
supports SWR for response
|
|
145
|
-
entries
|
|
216
|
+
Store support is layer-specific. `CFCacheStore` and `VercelCacheStore` support
|
|
217
|
+
SWR for segment, document/response, item (`"use cache"` and cached loaders), and
|
|
218
|
+
PPR shell entries. `MemorySegmentCacheStore` supports SWR for response, item,
|
|
219
|
+
and shell entries, but its route-segment entries expire at `ttl` (the `swr`
|
|
220
|
+
window is dropped) and never background-revalidate. Use the memory store for
|
|
146
221
|
local/dev behavior, not as proof that segment SWR is active.
|
|
147
222
|
|
|
148
223
|
## Key Differences
|
|
149
224
|
|
|
150
|
-
| | `cache()` DSL | `"use cache"` directive
|
|
151
|
-
| -------------------- | ----------------------------------------------------- |
|
|
152
|
-
| **Scope** | Route segment tree (handler + children + parallels) | Single function return value
|
|
153
|
-
| **Defined at** | Route definition site (`urls.ts`) | Inside function body or at file top
|
|
154
|
-
| **Cache key** | Request type + pathname + params
|
|
155
|
-
| **Execution on hit** | All-or-nothing: entire handler skipped | Partial: function body skipped, calling code runs
|
|
156
|
-
| **Runtime control** | `condition` to disable, custom `key` function | None — if the directive is present, it caches
|
|
157
|
-
| **Side effects** | Response side effects throw inside the boundary | `ctx.
|
|
158
|
-
| **Handle data** |
|
|
159
|
-
| **Loaders** | Always fresh — excluded from cache, opt-in per loader | Can be used inside loaders
|
|
160
|
-
| **Nesting** |
|
|
225
|
+
| | `cache()` DSL | `"use cache"` directive |
|
|
226
|
+
| -------------------- | ----------------------------------------------------- | ------------------------------------------------- |
|
|
227
|
+
| **Scope** | Route segment tree (handler + children + parallels) | Single function return value |
|
|
228
|
+
| **Defined at** | Route definition site (`urls.ts`) | Inside function body or at file top |
|
|
229
|
+
| **Cache key** | Request type + host + pathname + params + search | Function identity + serialized non-tainted args |
|
|
230
|
+
| **Execution on hit** | All-or-nothing: entire handler skipped | Partial: function body skipped, calling code runs |
|
|
231
|
+
| **Runtime control** | `condition` to disable, custom `key` function | None — if the directive is present, it caches |
|
|
232
|
+
| **Side effects** | Response side effects throw inside the boundary | `ctx.headers.set()`, `ctx.set()`, etc. throw |
|
|
233
|
+
| **Handle data** | Handler pushes replayed; loader pushes re-run live | Captured and replayed when it receives `ctx` |
|
|
234
|
+
| **Loaders** | Always fresh — excluded from cache, opt-in per loader | Can be used inside loaders |
|
|
235
|
+
| **Nesting** | Inner TTLs override; `key()` partitions compose | Compose by calling cached functions from uncached |
|
|
161
236
|
|
|
162
237
|
### cache() Cache Key
|
|
163
238
|
|
|
164
|
-
The key is `{requestType}:{pathname}:
|
|
165
|
-
`doc
|
|
166
|
-
for full document loads, client navigations, and intercept
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
`
|
|
239
|
+
The default key is `{requestType}:{host}{pathname}[:params][?search]`, where
|
|
240
|
+
requestType is `doc`, `partial`, or `intercept`. The same URL is therefore cached
|
|
241
|
+
separately for full document loads, client navigations, and intercept
|
|
242
|
+
navigations. The host keeps tenants apart when one deployment serves several
|
|
243
|
+
domains; the search part is sorted, excludes the router's internal params, and
|
|
244
|
+
honors `createRouter({ cache: { searchParams } })`.
|
|
245
|
+
|
|
246
|
+
A custom `key` function replaces the whole default key (e.g., to key by user role
|
|
247
|
+
or locale); it also bypasses the store's `keyGenerator` and the search-param
|
|
248
|
+
filter. It runs once per request and may read `cookies()`. A nested `cache()`
|
|
249
|
+
keys its records within the enclosing `key()` partition (`/caching` → "Keys
|
|
250
|
+
nest"). Never return raw request input from `key()`: normalize it to the
|
|
251
|
+
values you serve or prefix it. On a `ppr` route, `key()` (and a store
|
|
252
|
+
`keyGenerator` that returns a non-default key) also partitions the shell: one
|
|
253
|
+
shell per value, and a partitioned request never reads a build-time shell.
|
|
254
|
+
Keep the values to a small set. `condition` can disable caching entirely at
|
|
255
|
+
runtime (e.g., skip for authenticated users).
|
|
170
256
|
|
|
171
257
|
### "use cache" Cache Key
|
|
172
258
|
|
|
173
259
|
The key is `use-cache:{functionId}:{serializedArgs}` where functionId is a stable
|
|
174
|
-
ID from the Vite transform (module path + export name) and args are serialized
|
|
175
|
-
|
|
260
|
+
ID from the Vite transform (module path + export name) and args are serialized
|
|
261
|
+
(stable JSON when every arg is JSON-safe, RSC `encodeReply()` otherwise).
|
|
262
|
+
Request-scoped arguments are excluded: route fields read off `ctx` (handler,
|
|
263
|
+
loader or middleware: host, route name, pathname, params, search) and a
|
|
264
|
+
`Request`'s URL are folded in, and `env`
|
|
265
|
+
is left out. React element slots stay out of the key; any other argument that
|
|
266
|
+
cannot be serialized (a function, a class instance) runs the call uncached. See
|
|
267
|
+
`/use-cache`.
|
|
176
268
|
|
|
177
269
|
## Execution Model
|
|
178
270
|
|
|
@@ -180,17 +272,22 @@ This is the most important distinction.
|
|
|
180
272
|
|
|
181
273
|
### cache() — all-or-nothing
|
|
182
274
|
|
|
183
|
-
On cache hit, the cache-lookup middleware short-circuits
|
|
184
|
-
|
|
185
|
-
|
|
275
|
+
On cache hit, the cache-lookup middleware short-circuits segment resolution for
|
|
276
|
+
the boundary: no handler inside it runs. Segments above the boundary are not in
|
|
277
|
+
the entry and resolve as on an uncached render. On miss, all handlers execute
|
|
278
|
+
normally and the boundary's segments are stored.
|
|
186
279
|
|
|
187
280
|
```
|
|
188
|
-
HIT → cached segments served, loaders resolved fresh, no handler runs
|
|
189
|
-
MISS → all handlers run, segments cached, response built normally
|
|
281
|
+
HIT → segments above the boundary resolved, cached segments served, loaders resolved fresh, no handler in the boundary runs
|
|
282
|
+
MISS → all handlers run, the boundary's segments cached, response built normally
|
|
190
283
|
```
|
|
191
284
|
|
|
192
|
-
|
|
193
|
-
|
|
285
|
+
`ctx.set()` calls are safe: every handler that could read the value is inside the
|
|
286
|
+
same cached unit, so a hit replays a consistent subtree. Response side effects
|
|
287
|
+
and request-scoped reads are not safe — a write would reach only MISS responses
|
|
288
|
+
and a `cookies()` read would be baked into the shared entry — so the boundary
|
|
289
|
+
guard throws on them even on a miss (see "Correctness & invalidation" above and
|
|
290
|
+
"Headers and Cookies" below).
|
|
194
291
|
|
|
195
292
|
### "use cache" — partial execution
|
|
196
293
|
|
|
@@ -203,11 +300,13 @@ HIT → function body skipped, calling code runs, handle data replayed
|
|
|
203
300
|
MISS → function body runs, return value + handle data cached
|
|
204
301
|
```
|
|
205
302
|
|
|
206
|
-
Runtime guards throw if you call cookies()
|
|
207
|
-
ctx.
|
|
208
|
-
|
|
209
|
-
cache
|
|
210
|
-
|
|
303
|
+
Runtime guards throw if you call `cookies()`, `headers()`, `ctx.set()`,
|
|
304
|
+
`ctx.headers.set()` (or any response write: cookie writes, `setStatus()`,
|
|
305
|
+
`onResponse()`), `ctx.setTheme()`, or `ctx.setLocationState()` inside a
|
|
306
|
+
`"use cache"` function. `cookies()` and `headers()` are blocked because
|
|
307
|
+
per-request data is not in the cache key. Side-effect methods are blocked because
|
|
308
|
+
their effects are lost on hit. Use `ctx.use(Handle)` instead for data — handle
|
|
309
|
+
data is captured and replayed.
|
|
211
310
|
|
|
212
311
|
## When to Use cache()
|
|
213
312
|
|
|
@@ -230,6 +329,11 @@ export const urlpatterns = urls(({ path, cache }) => [
|
|
|
230
329
|
]);
|
|
231
330
|
```
|
|
232
331
|
|
|
332
|
+
On a `ppr` route, `cache(false)` or a `condition()` that returns false also
|
|
333
|
+
means no shell: that route (or that request) renders like a cache miss, with
|
|
334
|
+
no shell served or captured. A `ppr` route cannot opt out of a layout's
|
|
335
|
+
`cache()` and still get a shell.
|
|
336
|
+
|
|
233
337
|
## When to Use "use cache"
|
|
234
338
|
|
|
235
339
|
Use the `"use cache"` directive when:
|
|
@@ -250,10 +354,11 @@ async function getProductData(slug: string) {
|
|
|
250
354
|
return await db.query("SELECT * FROM products WHERE slug = ?", [slug]);
|
|
251
355
|
}
|
|
252
356
|
|
|
253
|
-
// Handler calls cached function, sets headers outside
|
|
357
|
+
// Handler calls cached function, sets headers outside it
|
|
358
|
+
// (this route is not inside a cache() boundary or a ppr route)
|
|
254
359
|
async function ProductPage(ctx) {
|
|
255
360
|
const data = await getProductData(ctx.params.slug);
|
|
256
|
-
ctx.
|
|
361
|
+
ctx.headers.set("X-Product", data.id);
|
|
257
362
|
return <Product data={data} />;
|
|
258
363
|
}
|
|
259
364
|
```
|
|
@@ -299,21 +404,39 @@ does **not** keep a value fresher than its parent:
|
|
|
299
404
|
So `"use cache: short"` (60s) inside `cache({ ttl: 600 })` yields ~600s freshness
|
|
300
405
|
on hits, **not** 60s. This is not a bug: setting `cache({ ttl: 600 })` declares
|
|
301
406
|
"this subtree may be ~600s stale." **If a value must be fresher than its
|
|
302
|
-
enclosing segment, put it in a loader**
|
|
303
|
-
|
|
407
|
+
enclosing segment, put it in a loader** read with `useLoader()` (without
|
|
408
|
+
`ssr: false` on a `ppr` route). `debugPerformance` prints cache hits per
|
|
409
|
+
layer, so the actual per-request behavior is observable.
|
|
410
|
+
|
|
411
|
+
Keys and tags cross the nesting too:
|
|
412
|
+
|
|
413
|
+
- An inner `cache()` without `key` keys its records within the outer `key()`
|
|
414
|
+
partition; one with its own `key` composes both (`/caching` → "Keys nest").
|
|
415
|
+
- Tags that `cacheTag()` calls and `"use cache"` reads record inside a
|
|
416
|
+
`cache()` subtree are stored on that `cache()` entry, so `updateTag()` of one
|
|
417
|
+
of them evicts it (and a `ppr` shell or document built from it) instead of
|
|
418
|
+
waiting for it to expire.
|
|
419
|
+
- A `ppr` shell never outlives the route `cache()` entry it was captured
|
|
420
|
+
from: its ttl and ttl + swr are capped to what that entry has left.
|
|
304
421
|
|
|
305
422
|
## Headers and Cookies
|
|
306
423
|
|
|
307
424
|
Neither mechanism caches response headers or cookies.
|
|
308
425
|
|
|
309
426
|
- **cache()**: Response-level side effects throw inside the cache boundary even
|
|
310
|
-
on a miss: `ctx.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
427
|
+
on a miss: `ctx.headers` mutation (`ctx.headers.set()` etc.), `cookies()`
|
|
428
|
+
(read or write), and the request-context writers `header()`, `setCookie()`,
|
|
429
|
+
`deleteCookie()`, `setStatus()`, `onResponse()`. On a hit the handler would be
|
|
430
|
+
skipped, so allowing the write on a miss would produce inconsistent responses.
|
|
431
|
+
Registered loaders are exempt (they run on every request, hits included). If
|
|
432
|
+
you need headers or cookies on every response, set them in middleware, a live
|
|
433
|
+
segment outside the cache boundary, or a loader.
|
|
434
|
+
- **"use cache"**: `cookies()` and `headers()` throw inside the cached function
|
|
435
|
+
(both reads and writes), and so do `ctx.headers` mutations. Move them outside.
|
|
436
|
+
- **`ctx.theme`** (handler and middleware) and **`getRequestContext().theme`**
|
|
437
|
+
are the visitor's theme cookie, so a read throws in both scopes, like
|
|
438
|
+
`cookies()`. Read it with `useTheme()` in a client component, or in a
|
|
439
|
+
loader (see the `/theme` skill).
|
|
317
440
|
|
|
318
441
|
```typescript
|
|
319
442
|
// Set headers that must appear on every response in middleware
|
|
@@ -327,8 +450,9 @@ middleware(async (ctx, next) => {
|
|
|
327
450
|
|
|
328
451
|
Context variables created with `createVar()` are cacheable by default and can
|
|
329
452
|
be read freely inside cached scopes. A non-cacheable var throws when read
|
|
330
|
-
**directly** with `ctx.get()` inside a `cache()` boundary
|
|
331
|
-
otherwise be serialized into the stored
|
|
453
|
+
**directly** with `ctx.get()` inside a `cache()` boundary or a `"use cache"`
|
|
454
|
+
function — where the value would otherwise be serialized into the stored
|
|
455
|
+
segment or entry without being part of its key.
|
|
332
456
|
|
|
333
457
|
There are two ways to mark a value as non-cacheable:
|
|
334
458
|
|
|
@@ -345,30 +469,36 @@ specifies `cache: false`, the value is non-cacheable.
|
|
|
345
469
|
|
|
346
470
|
**Behavior inside a `cache()` boundary:**
|
|
347
471
|
|
|
348
|
-
| Operation
|
|
349
|
-
|
|
|
350
|
-
| `cookies()` / `headers()` (read or write)
|
|
351
|
-
| `ctx.
|
|
352
|
-
| `ctx.get(
|
|
353
|
-
| `ctx.
|
|
354
|
-
| `ctx.
|
|
355
|
-
|
|
|
472
|
+
| Operation | Inside a `cache()` boundary |
|
|
473
|
+
| -------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
474
|
+
| `cookies()` / `headers()` (read or write) | Throws (request-scoped, would poison the shared entry) |
|
|
475
|
+
| `ctx.theme`, `getRequestContext().theme` | Throws, like `cookies()` (#971) |
|
|
476
|
+
| `ctx.get(cacheableVar)` | Allowed |
|
|
477
|
+
| `ctx.get(nonCacheableVar)` | Throws (would be baked in) |
|
|
478
|
+
| `ctx.set(var, value)` (cacheable) | Allowed |
|
|
479
|
+
| `ctx.headers.set()` / cookie writes | Throws (response side effect would be lost on hit) |
|
|
480
|
+
| Any of the above **inside a loader** | Allowed (loaders always run fresh) |
|
|
481
|
+
| The reads above in a loader with its own `cache()` | The miss fails without a `key()`; any `key()` allows them, so it must include them |
|
|
356
482
|
|
|
357
483
|
(Both scopes block the same request-scoped APIs — `cookies()`, `headers()`,
|
|
358
484
|
response side effects, and non-cacheable `ctx.get()` — because each would leak
|
|
359
485
|
per-request data into a shared cache entry. The `cache()` boundary tracks the
|
|
360
486
|
scope via `isInsideCacheScope()`; `"use cache"` uses the exec guard and also
|
|
361
487
|
excludes tainted `ctx`/`env`/`req` args from the cache key. Loaders are exempt in
|
|
362
|
-
both — see "Headers and Cookies" and the precise guarantee below.
|
|
488
|
+
both — see "Headers and Cookies" and the precise guarantee below. Under `"use
|
|
489
|
+
cache"` the exemption covers a loader body the cached function consumes
|
|
490
|
+
(`await ctx.use(Loader)`), not a cached function a loader calls: the loader
|
|
491
|
+
re-runs on every request, the cached body does not.)
|
|
363
492
|
|
|
364
493
|
Write is dumb — `ctx.set()` stores the cache metadata but does not enforce.
|
|
365
494
|
Enforcement happens at read time (`ctx.get()`), where ALS detects the cache
|
|
366
495
|
scope and rejects non-cacheable reads.
|
|
367
496
|
|
|
368
|
-
### The guarantee is precise — a direct read inside
|
|
497
|
+
### The guarantee is precise — a direct read inside a cache scope, not propagating
|
|
369
498
|
|
|
370
499
|
The guard fires on a **direct** `ctx.get(taintedVar)` **inside a `cache()`
|
|
371
|
-
boundary** (the scope `isInsideCacheScope` detects)
|
|
500
|
+
boundary** (the scope `isInsideCacheScope` detects) **or a `"use cache"`
|
|
501
|
+
function body**. The taint lives on the
|
|
372
502
|
variable; a value **derived** from it and read **outside** the boundary is not
|
|
373
503
|
tracked:
|
|
374
504
|
|
|
@@ -396,35 +526,47 @@ layout((ctx) => {
|
|
|
396
526
|
So do **not** read this as "you can't cache user data" — that overstates it and
|
|
397
527
|
breeds the false confidence that makes the derived leak _more_ likely. The guard
|
|
398
528
|
is deliberately non-propagating (propagation would cost a wrapper per derivation
|
|
399
|
-
on the hot path), and it is scoped to the `cache()` segment boundary
|
|
400
|
-
cache"` functions block the same request-scoped reads
|
|
401
|
-
|
|
402
|
-
|
|
529
|
+
on the hot path), and it is scoped to the `cache()` segment boundary and the
|
|
530
|
+
`"use cache"` body. `"use cache"` functions block the same request-scoped reads
|
|
531
|
+
(`cookies()` / `headers()` and non-cacheable `ctx.get()` throw inside them) and
|
|
532
|
+
additionally exclude tainted `ctx`/`env`/`req` args from the cache key — pass a
|
|
533
|
+
non-cacheable value in as an argument so it becomes part of the key. The pattern that stays safe is also the natural one:
|
|
403
534
|
**read tainted context at the point of use, in the path that needs it (a loader or
|
|
404
535
|
live segment) — never extract user data into a plain value and cache that.**
|
|
405
536
|
Loaders are exempt because they run outside the cache scope and resolve fresh
|
|
406
|
-
every request.
|
|
537
|
+
every request. A loader with its own `cache()` does not: its value is stored,
|
|
538
|
+
so it falls under the loader `cache()` guard above.
|
|
407
539
|
|
|
408
540
|
## Loaders Are Always Fresh
|
|
409
541
|
|
|
410
|
-
Loaders are **never cached** by route-level `cache()`. Even on a
|
|
411
|
-
where
|
|
412
|
-
every request. This is enforced at two levels:
|
|
413
|
-
|
|
414
|
-
1. **Storage**: `cacheRoute()` filters out loader segments before serialization
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
This means `cache()` gives you cached UI + fresh data by default
|
|
420
|
-
|
|
542
|
+
Loaders are **never cached** by route-level `cache()`. Even on a cache hit
|
|
543
|
+
where the boundary's UI segments are served from cache, loaders are re-resolved
|
|
544
|
+
fresh on every request. This is enforced at two levels:
|
|
545
|
+
|
|
546
|
+
1. **Storage**: `cacheRoute()` filters out loader segments before serialization.
|
|
547
|
+
2. **Retrieval**: On cache hit, `resolveLoadersOnly()` runs the boundary's
|
|
548
|
+
loaders after yielding its cached UI segments (the loaders above the boundary
|
|
549
|
+
run with their live segments), ensuring fresh data regardless of cache state.
|
|
550
|
+
|
|
551
|
+
This means `cache()` gives you cached UI + fresh data by default, where the
|
|
552
|
+
data is read outside the cached output: `useLoader()` in a client component.
|
|
553
|
+
A handler inside the boundary that awaits `ctx.use(Loader)` renders the value
|
|
554
|
+
it got on the miss, and every hit replays that output. On a `ppr` route an
|
|
555
|
+
`ssr: false` loader is the other exception: it bakes into the shell (`/ppr`).
|
|
556
|
+
To also cache a loader's data, explicitly opt in with
|
|
557
|
+
`loader(Fn, () => [cache({...})])`.
|
|
558
|
+
That entry is keyed by loader, host, path and params, not by the route's
|
|
559
|
+
`key()`, so give it a `key()` that includes any request data its body reads;
|
|
560
|
+
without one the miss fails.
|
|
421
561
|
|
|
422
562
|
## cache() Placement Patterns
|
|
423
563
|
|
|
424
|
-
###
|
|
564
|
+
### cache() among a path's children
|
|
425
565
|
|
|
426
|
-
|
|
427
|
-
|
|
566
|
+
A `cache()` among a path's children caches that path. The path's handler and
|
|
567
|
+
its own layouts and parallels are one cached unit, wherever the `cache()` sits
|
|
568
|
+
in the list. Every other item still attaches to the path, and the segments
|
|
569
|
+
above the path stay live:
|
|
428
570
|
|
|
429
571
|
```typescript
|
|
430
572
|
path("/dashboard", DashboardPage, { name: "dashboard" }, () => [
|
|
@@ -433,34 +575,66 @@ path("/dashboard", DashboardPage, { name: "dashboard" }, () => [
|
|
|
433
575
|
parallel("@stats", StatsPanel),
|
|
434
576
|
parallel("@activity", ActivityFeed),
|
|
435
577
|
]),
|
|
578
|
+
loader(DashboardLoader),
|
|
436
579
|
]),
|
|
437
580
|
```
|
|
438
581
|
|
|
439
582
|
On hit: DashboardPage, DashboardSidebar, StatsPanel, and ActivityFeed are all
|
|
440
|
-
served from cache. On miss: all handlers run
|
|
583
|
+
served from cache, and DashboardLoader runs fresh. On miss: all handlers run and
|
|
584
|
+
the path's segments are cached together. DashboardPage runs inside the boundary,
|
|
585
|
+
so the guards apply to it: a header write there throws. The wrapper form
|
|
586
|
+
`cache({ ttl: 300 }, () => [layout(DashboardSidebar)])` inside a path does the
|
|
587
|
+
same, and `cache(false)` there opts the path out of an enclosing `cache()` (on a
|
|
588
|
+
`ppr` route, out of the shell too). The same form caches a response route:
|
|
589
|
+
`path.json("/api/feed", handler, { name: "feed" }, () => [cache({ ttl: 60 })])`.
|
|
590
|
+
A `cache()` inside a `layout()` or `middleware()` wrapper in the path caches the
|
|
591
|
+
path too: `layout(DashboardSidebar, () => [cache({ ttl: 300 })])` is the same
|
|
592
|
+
unit as the example above, not the sidebar alone.
|
|
593
|
+
|
|
594
|
+
### Bare cache() among a layout's children
|
|
595
|
+
|
|
596
|
+
A `cache()` with no children callback covers the routes after it. The
|
|
597
|
+
`layout()`, `parallel()`, `loader()` and `middleware()` items after it still
|
|
598
|
+
wrap every route of the layout, including the routes before the `cache()`;
|
|
599
|
+
they are cached only with the routes after it:
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
layout(<AppShell />, () => [
|
|
603
|
+
path("/a", PageA, { name: "a" }), // PromoBanner renders live on /a
|
|
604
|
+
cache({ ttl: 60 }),
|
|
605
|
+
layout(<PromoBanner />),
|
|
606
|
+
path("/b", PageB, { name: "b" }), // PromoBanner cached with /b
|
|
607
|
+
]),
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
With no route after the `cache()`, nothing is cached. To cache
|
|
611
|
+
`<PromoBanner />` with PageA too, put the `cache()` before PageA's `path()`.
|
|
612
|
+
A `cache()` among PageA's own children does not cover it: the banner belongs
|
|
613
|
+
to the layout, above that path's boundary.
|
|
441
614
|
|
|
442
615
|
### Uncached layout with cached children
|
|
443
616
|
|
|
444
617
|
The cache boundary only covers what's inside it. Parent segments above the
|
|
445
|
-
boundary are not cached and always re-render:
|
|
618
|
+
boundary are not cached and always re-render, cache hits included:
|
|
446
619
|
|
|
447
620
|
```typescript
|
|
448
621
|
layout(RootLayout, () => [
|
|
449
622
|
// RootLayout is NOT cached — runs every request
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
623
|
+
cache({ ttl: 300 }, () => [
|
|
624
|
+
path("/products/:slug", ProductPage, { name: "product" }, () => [
|
|
625
|
+
parallel("@reviews", ReviewsPanel),
|
|
626
|
+
parallel("@related", RelatedProducts),
|
|
627
|
+
]),
|
|
455
628
|
]),
|
|
456
629
|
]),
|
|
457
630
|
```
|
|
458
631
|
|
|
459
|
-
RootLayout renders fresh every request
|
|
460
|
-
ReviewsPanel, and RelatedProducts are
|
|
461
|
-
served from cache on hit. This is useful when the root layout
|
|
462
|
-
request-specific data (user session, theme) but the product content
|
|
463
|
-
cacheable.
|
|
632
|
+
RootLayout renders fresh every request, and a header it writes lands on every
|
|
633
|
+
response. ProductPage, ReviewsPanel, and RelatedProducts are inside the cache
|
|
634
|
+
boundary and served from cache on hit. This is useful when the root layout
|
|
635
|
+
depends on request-specific data (user session, theme) but the product content
|
|
636
|
+
is cacheable. On a `ppr` route the shell bakes RootLayout too: a shell HIT runs
|
|
637
|
+
no layout, and the shell expires no later than this `cache()` entry.
|
|
464
638
|
|
|
465
639
|
### Loader-level caching
|
|
466
640
|
|
|
@@ -479,14 +653,22 @@ path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
|
479
653
|
```
|
|
480
654
|
|
|
481
655
|
This attaches the cache config directly to the loader entry. The loader's
|
|
482
|
-
data is cached independently from the route's segment cache
|
|
656
|
+
data is cached independently from the route's segment cache, together with
|
|
657
|
+
the handle pushes its body made (replayed on every hit). Loader caching
|
|
483
658
|
supports custom keys, tags, SWR, conditional bypass, and per-loader store
|
|
484
|
-
overrides — see `/loader` for the full reference.
|
|
659
|
+
overrides — see `/loader` for the full reference. A body that reads
|
|
660
|
+
`cookies()`, `headers()` or a non-cacheable variable needs a `key()`, which
|
|
661
|
+
must include the value: without one the miss fails, and any `key()` switches
|
|
662
|
+
that check off. The entry also carries the tags its body recorded
|
|
663
|
+
(`cacheTag()` calls, `"use cache"` reads, and those of the loaders it reads
|
|
664
|
+
with `ctx.use()`), so `updateTag()` of any of them drops it.
|
|
485
665
|
|
|
486
666
|
## See Also
|
|
487
667
|
|
|
488
|
-
- `/caching` — cache() DSL setup, stores, nested boundaries
|
|
668
|
+
- `/caching` — cache() DSL setup, stores, tags, nested boundaries
|
|
489
669
|
- `/use-cache` — "use cache" directive details, profiles, transforms, guards
|
|
670
|
+
- `/loader` — loader-level caching and the loader context
|
|
671
|
+
- `/prerender` — build-time segments with `Prerender()`/`Static()`
|
|
672
|
+
- `/ppr` — PPR shell caching: cached HTML shell + live holes (different layer)
|
|
490
673
|
- `/document-cache` — store-backed complete-response middleware
|
|
491
674
|
- `/deployment-caching` — in-function versus external CDN cache boundaries
|
|
492
|
-
- `/ppr` — PPR shell caching: cached HTML shell + live loader holes (different layer)
|