@rangojs/router 0.34.0 → 0.35.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 +1 -1
- package/dist/bin/{chunk-4HHFQSDC.js → chunk-3CQXDX3A.js} +5 -5
- package/dist/bin/{client-urls-projection-4Q5EQDAL.js → client-urls-projection-2GBDM5YK.js} +1 -1
- package/dist/bin/rango.js +1 -1
- package/dist/bin/{runtime-discovery-2ZTYRZ3V.js → runtime-discovery-PA3V3IFQ.js} +2 -2
- package/dist/bin/{temp-rsc-server-EUNKRFJD.js → temp-rsc-server-WDXEHDY7.js} +2 -2
- package/dist/types/cache/shell-snapshot.d.ts +1 -1
- package/dist/types/cache/types.d.ts +1 -1
- package/dist/types/client-urls/server-projection.d.ts +1 -1
- package/dist/types/client-urls/types.d.ts +6 -6
- package/dist/types/decode-loader-results.d.ts +1 -1
- package/dist/types/errors.d.ts +3 -3
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/loader-store.d.ts +20 -8
- package/dist/types/outlet-context.d.ts +1 -1
- package/dist/types/route-definition/helpers-types.d.ts +5 -5
- package/dist/types/router/loader-resolution.d.ts +1 -1
- package/dist/types/router/match-context.d.ts +12 -0
- package/dist/types/router/navigation-snapshot.d.ts +17 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +7 -7
- package/dist/types/router/types.d.ts +1 -1
- package/dist/types/rsc/shell-capture-constants.d.ts +2 -2
- package/dist/types/server/context.d.ts +3 -3
- package/dist/types/server/request-context.d.ts +1 -1
- package/dist/types/ssr/index.d.ts +1 -1
- package/dist/types/ssr/ssr-root.d.ts +1 -1
- package/dist/types/ssr-suspension-warning.d.ts +1 -1
- package/dist/types/types/loader-types.d.ts +19 -19
- package/dist/types/types/segments.d.ts +2 -2
- package/dist/types/urls/path-helper-types.d.ts +4 -4
- package/dist/types/urls/pattern-types.d.ts +1 -1
- package/dist/types/vite/plugin-types.d.ts +1 -1
- package/dist/vite/index.js +7 -7
- package/package.json +1 -1
- package/skills/app-structure/e-commerce.md +1 -1
- package/skills/breadcrumbs/SKILL.md +5 -5
- package/skills/cache-guide/SKILL.md +4 -4
- package/skills/caching/SKILL.md +1 -1
- package/skills/catalog.json +2 -2
- package/skills/client-urls/SKILL.md +20 -20
- package/skills/comparison/references/framework-comparison.md +6 -6
- package/skills/defer-hydration/SKILL.md +2 -2
- package/skills/handler-use/SKILL.md +5 -5
- package/skills/hooks/data.md +5 -2
- package/skills/hooks/handle-and-actions.md +9 -9
- package/skills/loader/SKILL.md +52 -47
- package/skills/middleware/SKILL.md +2 -2
- package/skills/migrate-nextjs/SKILL.md +10 -10
- package/skills/migrate-react-router/SKILL.md +1 -1
- package/skills/migrate-react-router/component-migration.md +2 -2
- package/skills/migrate-react-router/data-and-actions.md +2 -2
- package/skills/migrate-react-router/route-mapping.md +2 -2
- package/skills/parallel/SKILL.md +2 -2
- package/skills/ppr/SKILL.md +91 -92
- package/skills/prerender/SKILL.md +5 -5
- package/skills/rango/SKILL.md +7 -7
- package/skills/route/SKILL.md +1 -1
- package/skills/router-setup/SKILL.md +2 -2
- package/skills/scripts/SKILL.md +3 -3
- package/skills/shell-manifest/SKILL.md +3 -3
- package/skills/testing/serve-shell-request.md +1 -1
- package/skills/theme/SKILL.md +2 -2
- package/skills/typesafety/env-and-bindings.md +2 -2
- package/skills/use-cache/SKILL.md +1 -1
- package/skills/ux-architecture/SKILL.md +7 -7
- package/src/cache/shell-snapshot.ts +1 -1
- package/src/cache/types.ts +1 -1
- package/src/client-urls/client-urls.ts +16 -9
- package/src/client-urls/server-projection.ts +4 -4
- package/src/client-urls/types.ts +6 -6
- package/src/decode-loader-results.ts +1 -1
- package/src/errors.ts +9 -9
- package/src/handles/meta.ts +2 -2
- package/src/loader-store.ts +29 -13
- package/src/outlet-context.ts +1 -1
- package/src/route-definition/dsl-helpers.ts +4 -8
- package/src/route-definition/helpers-types.ts +5 -5
- package/src/router/loader-resolution.ts +4 -4
- package/src/router/match-api.ts +2 -0
- package/src/router/match-context.ts +22 -1
- package/src/router/match-middleware/cache-lookup.ts +17 -8
- package/src/router/match-middleware/intercept-resolution.ts +3 -3
- package/src/router/navigation-snapshot.ts +39 -8
- package/src/router/segment-resolution/loader-cache.ts +8 -8
- package/src/router/segment-resolution/prefetch-deferral.ts +1 -1
- package/src/router/segment-resolution/revalidation.ts +3 -3
- package/src/router/types.ts +1 -1
- package/src/rsc/shell-capture-constants.ts +3 -3
- package/src/rsc/shell-capture.ts +12 -12
- package/src/segment-system.tsx +3 -3
- package/src/server/context.ts +5 -5
- package/src/server/cookie-store.ts +1 -1
- package/src/server/request-context.ts +1 -1
- package/src/ssr/index.tsx +6 -6
- package/src/ssr/ssr-root.tsx +1 -1
- package/src/ssr-suspension-warning.ts +4 -4
- package/src/types/loader-types.ts +19 -19
- package/src/types/segments.ts +2 -2
- package/src/urls/path-helper-types.ts +4 -4
- package/src/urls/pattern-types.ts +1 -1
- package/src/use-loader.tsx +5 -3
- package/src/vite/discovery/shell-prerender-phase.ts +1 -1
- package/src/vite/plugin-types.ts +1 -1
package/skills/loader/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: loader
|
|
3
|
-
description: Define data loaders with createLoader and register them on routes with loader(). Use when a route needs per-request data that stays fresh and streams while the page renders, a client component reads server data with useLoader, a loader should re-run after specific actions (revalidate), be cached (cache), be callable from the client (fetchable loaders), throw notFound()/redirect(), write page meta/breadcrumbs handles, must be settled in the SSR'd document (ssr:
|
|
3
|
+
description: Define data loaders with createLoader and register them on routes with loader(). Use when a route needs per-request data that stays fresh and streams while the page renders, a client component reads server data with useLoader, a loader should re-run after specific actions (revalidate), be cached (cache), be callable from the client (fetchable loaders), throw notFound()/redirect(), write page meta/breadcrumbs handles, must be settled in the SSR'd document (ssr:"await"), or must stay out of link prefetches (prefetch:false).
|
|
4
4
|
argument-hint: "[loader]"
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ argument-hint: "[loader]"
|
|
|
8
8
|
|
|
9
9
|
A loader is a server function created with `createLoader()` and registered on a
|
|
10
10
|
route segment with `loader()`. It runs fresh on every request (on a `ppr` route,
|
|
11
|
-
one registered with `ssr:
|
|
11
|
+
one registered with `ssr: "await"` is baked into the shell instead), streams its
|
|
12
12
|
result to the client, and client components read it with `useLoader()`. This
|
|
13
13
|
skill covers defining, registering, consuming, revalidating, caching, and
|
|
14
14
|
client-fetching loaders.
|
|
@@ -130,15 +130,15 @@ path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
|
130
130
|
DSL loaders are the **live data layer**: they resolve fresh on every request,
|
|
131
131
|
even when the route is inside a `cache()` boundary, so `cache()` gives you
|
|
132
132
|
cached UI + fresh data by default. See "Loaders: The Live Data Layer" below.
|
|
133
|
-
The exception is a `ppr` route's `ssr:
|
|
133
|
+
The exception is a `ppr` route's `ssr: "await"` loader: it bakes into the shell,
|
|
134
134
|
and a shell HIT runs it only when its return carries promises.
|
|
135
135
|
|
|
136
136
|
### Cache safety
|
|
137
137
|
|
|
138
|
-
A DSL loader without `ssr:
|
|
138
|
+
A DSL loader without `ssr: "await"` can read `createVar({ cache: false })`
|
|
139
139
|
variables: it runs on every request, `cache()` and PPR shell HITs included, so
|
|
140
140
|
the read guard is bypassed for it. On a `ppr` route the capture runs an
|
|
141
|
-
`ssr:
|
|
141
|
+
`ssr: "await"` loader, and a loader a handler awaits, and bakes their values into
|
|
142
142
|
the shell: a `cookies()`, `headers()`, `ctx.request.headers` or
|
|
143
143
|
`{ cache: false }` read there refuses the capture, so the route keeps
|
|
144
144
|
rendering without a shell (warned once per key).
|
|
@@ -191,7 +191,7 @@ the tree).
|
|
|
191
191
|
- The same holds under a PPR shell capture (`/ppr`): handler consumption is the
|
|
192
192
|
BAKED lane — the loader executes at capture and the rendered value is a
|
|
193
193
|
capture-time copy served on every HIT (a HIT never runs the handler);
|
|
194
|
-
`useLoader` of a loader without `ssr:
|
|
194
|
+
`useLoader` of a loader without `ssr: "await"` is the live lane (an `ssr: "await"`
|
|
195
195
|
loader is the bake lane however it is read). Unlike `cache()`, an identity
|
|
196
196
|
read inside that loader (`cookies()`, `headers()`, `ctx.request.headers`, a
|
|
197
197
|
`{ cache: false }` variable) refuses the capture, so the route stays
|
|
@@ -226,26 +226,26 @@ never sends it (see `/hooks` `state.md`, "State on a document response").
|
|
|
226
226
|
|
|
227
227
|
### Full field surface
|
|
228
228
|
|
|
229
|
-
| Field | Type | Notes
|
|
230
|
-
| ------------------ | ------------------------------- |
|
|
231
|
-
| `params` | `TParams` | Merged route + explicit loader params; overridable by fetchable `load({ params })`.
|
|
232
|
-
| `routeParams` | `Record<string, string>` | Server-trusted route params from URL pattern matching; cannot be overridden.
|
|
233
|
-
| `request` | `Request` | The incoming `Request` (headers, method, body, `signal` for abort).
|
|
234
|
-
| `url` | `URL` | Request URL with internal `_rsc*` transport params stripped. Use this for application logic.
|
|
235
|
-
| `originalUrl` | `URL` | Request URL with every param intact, including `_rsc*`. Only for debugging or custom cache keying.
|
|
236
|
-
| `pathname` | `string` | URL pathname (shortcut for `ctx.url.pathname`).
|
|
237
|
-
| `searchParams` | `URLSearchParams` | Shortcut for `ctx.url.searchParams`.
|
|
238
|
-
| `search` | `ResolveSearchSchema<TSearch>` | Typed query params when a search schema is declared on the route; `{}` otherwise.
|
|
239
|
-
| `env` | `any` | Bindings from `createRouter<TEnv>()` (DB, KV, secrets, etc.). `createLoader` types it as `any`; annotate or cast at the use site.
|
|
240
|
-
| `waitUntil` | `(fn: () => Promise<void>)` | Run work after the response is sent. Takes a function, not a promise. Delegates to Cloudflare's `waitUntil`; fire-and-forget with error logging elsewhere.
|
|
241
|
-
| `executionContext` | `ExecutionContext \| undefined` | Raw Cloudflare `ExecutionContext` for libraries that need it; `undefined` off Cloudflare. Prefer `waitUntil`.
|
|
242
|
-
| `get` | `(key \| ContextVar \| handle)` | Reads middleware variables/context-vars — or READS a handle's collected data, after `await ctx.rendered()`.
|
|
243
|
-
| `use` | `(loader \| handle) => T` | Access another loader's data (Promise), or WRITE a handle: `ctx.use(Meta)({ title })` returns the push function — handler parity. Reads moved to `get`.
|
|
244
|
-
| `rendered` | `() => Promise<void>` | **Experimental.** DSL loaders only — waits for all non-loader segments (including `loading()` streaming handlers) to settle before reading handle data. Not with `ssr:
|
|
245
|
-
| `method` | `string` | HTTP method. `"GET"` for SSR loader runs; reflects real method for fetchable loaders.
|
|
246
|
-
| `body` | `TBody \| undefined` | Parsed request body for fetchable POST/PUT/PATCH/DELETE calls.
|
|
247
|
-
| `formData` | `FormData \| undefined` | Present when a fetchable loader is invoked via form submission.
|
|
248
|
-
| `reverse` | `AutofillAwareReverseFunction` | Generate type-checked URLs from route names (same scoped semantics as route handlers; params the matched route carries are optional).
|
|
229
|
+
| Field | Type | Notes |
|
|
230
|
+
| ------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
231
|
+
| `params` | `TParams` | Merged route + explicit loader params; overridable by fetchable `load({ params })`. |
|
|
232
|
+
| `routeParams` | `Record<string, string>` | Server-trusted route params from URL pattern matching; cannot be overridden. |
|
|
233
|
+
| `request` | `Request` | The incoming `Request` (headers, method, body, `signal` for abort). |
|
|
234
|
+
| `url` | `URL` | Request URL with internal `_rsc*` transport params stripped. Use this for application logic. |
|
|
235
|
+
| `originalUrl` | `URL` | Request URL with every param intact, including `_rsc*`. Only for debugging or custom cache keying. |
|
|
236
|
+
| `pathname` | `string` | URL pathname (shortcut for `ctx.url.pathname`). |
|
|
237
|
+
| `searchParams` | `URLSearchParams` | Shortcut for `ctx.url.searchParams`. |
|
|
238
|
+
| `search` | `ResolveSearchSchema<TSearch>` | Typed query params when a search schema is declared on the route; `{}` otherwise. |
|
|
239
|
+
| `env` | `any` | Bindings from `createRouter<TEnv>()` (DB, KV, secrets, etc.). `createLoader` types it as `any`; annotate or cast at the use site. |
|
|
240
|
+
| `waitUntil` | `(fn: () => Promise<void>)` | Run work after the response is sent. Takes a function, not a promise. Delegates to Cloudflare's `waitUntil`; fire-and-forget with error logging elsewhere. |
|
|
241
|
+
| `executionContext` | `ExecutionContext \| undefined` | Raw Cloudflare `ExecutionContext` for libraries that need it; `undefined` off Cloudflare. Prefer `waitUntil`. |
|
|
242
|
+
| `get` | `(key \| ContextVar \| handle)` | Reads middleware variables/context-vars — or READS a handle's collected data, after `await ctx.rendered()`. |
|
|
243
|
+
| `use` | `(loader \| handle) => T` | Access another loader's data (Promise), or WRITE a handle: `ctx.use(Meta)({ title })` returns the push function — handler parity. Reads moved to `get`. |
|
|
244
|
+
| `rendered` | `() => Promise<void>` | **Experimental.** DSL loaders only — waits for all non-loader segments (including `loading()` streaming handlers) to settle before reading handle data. Not with `ssr: "await"` (cycle; throws). |
|
|
245
|
+
| `method` | `string` | HTTP method. `"GET"` for SSR loader runs; reflects real method for fetchable loaders. |
|
|
246
|
+
| `body` | `TBody \| undefined` | Parsed request body for fetchable POST/PUT/PATCH/DELETE calls. |
|
|
247
|
+
| `formData` | `FormData \| undefined` | Present when a fetchable loader is invoked via form submission. |
|
|
248
|
+
| `reverse` | `AutofillAwareReverseFunction` | Generate type-checked URLs from route names (same scoped semantics as route handlers; params the matched route carries are optional). |
|
|
249
249
|
|
|
250
250
|
### Example
|
|
251
251
|
|
|
@@ -556,14 +556,14 @@ served from cache. Route-level `cache()` caches rendered segments but never
|
|
|
556
556
|
loader data: loaders are excluded when the segments are stored and re-resolved
|
|
557
557
|
when they are served. Caching a loader's own data is a separate opt-in (see
|
|
558
558
|
"Opting a Loader into Caching"). A `ppr` shell does store some loader data: an
|
|
559
|
-
`ssr:
|
|
560
|
-
loader only when its return carries promises (see "`ssr:
|
|
559
|
+
`ssr: "await"` loader's settled data bakes into the shell, and a HIT runs that
|
|
560
|
+
loader only when its return carries promises (see "`ssr: "await"`" below and
|
|
561
561
|
`/ppr`).
|
|
562
562
|
|
|
563
563
|
Pre-rendering follows the same rule: the build-time Flight payload skips
|
|
564
564
|
loaders (there is no real request context), and at runtime the worker resolves
|
|
565
565
|
them fresh against the live database. The exception is a `Prerender` + `ppr`
|
|
566
|
-
route: its build-time shell capture runs `ssr:
|
|
566
|
+
route: its build-time shell capture runs `ssr: "await"` loaders and bakes their
|
|
567
567
|
settled data into the shell, like a runtime capture.
|
|
568
568
|
|
|
569
569
|
### Parallel and streaming — latency overlaps first paint
|
|
@@ -584,9 +584,9 @@ boundary a parallel loader blocks its parent, so add one to keep the overlap.)
|
|
|
584
584
|
If you come from a framework where the loader is a blocking step that runs
|
|
585
585
|
before the response is built, this is the shift to internalize: here the
|
|
586
586
|
response starts streaming first and loader data fills in. (The one deliberate
|
|
587
|
-
exception is per-loader: `loader(Def, { ssr:
|
|
587
|
+
exception is per-loader: `loader(Def, { ssr: "await" })` awaits that
|
|
588
588
|
loader before first flush on document renders other than a `ppr` shell HIT,
|
|
589
|
-
where the shell flushes first — see "`ssr:
|
|
589
|
+
where the shell flushes first — see "`ssr: "await"`" below.)
|
|
590
590
|
|
|
591
591
|
### See it: `debugPerformance`
|
|
592
592
|
|
|
@@ -693,7 +693,7 @@ rejects) skips the whole entry, as above. Entries written
|
|
|
693
693
|
before handles were recorded replay none until they are rewritten (expiry or
|
|
694
694
|
SWR revalidation).
|
|
695
695
|
|
|
696
|
-
On a `ppr` route the shell keeps a copy of an `ssr:
|
|
696
|
+
On a `ppr` route the shell keeps a copy of an `ssr: "await"` loader's pushes
|
|
697
697
|
too, a pushed promise resolved (#1182). The two never disagree on the page:
|
|
698
698
|
where a replay of the shell serves the loader's data from the shell, the
|
|
699
699
|
shell's copy is the one shown and the entry only adds a push whose value
|
|
@@ -1005,12 +1005,12 @@ export const ProductLoader = createLoader(async (ctx) => {
|
|
|
1005
1005
|
|
|
1006
1006
|
Semantics by lane:
|
|
1007
1007
|
|
|
1008
|
-
| Signal | Document load, read inside `<Suspense>` or under `loading()`
|
|
1009
|
-
| ------------ |
|
|
1010
|
-
| `notFound()` | Not-found UI resolves server-side (nearest `notFoundBoundary` → router option → default) and rides the envelope; the 404 STATUS is **opportunistic** — real only if the rejection beats Response construction. `ssr:
|
|
1011
|
-
| `redirect()` | 200 document, then a client-side replace to the target
|
|
1008
|
+
| Signal | Document load, read inside `<Suspense>` or under `loading()` | Document load, read outside any `<Suspense>` | Client navigation |
|
|
1009
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
|
|
1010
|
+
| `notFound()` | Not-found UI resolves server-side (nearest `notFoundBoundary` → router option → default) and rides the envelope; the 404 STATUS is **opportunistic** — real only if the rejection beats Response construction. `ssr: "await"` (below) makes it deterministic. | A real 404; the browser renders the not-found UI with the layouts kept (the recovery document, below) | 404 UI swaps in, URL preserved, payload stays 200 |
|
|
1011
|
+
| `redirect()` | 200 document, then a client-side replace to the target | A real 3xx (the redirect's own status) with `Location`; with `state`, the recovery document (200), then the client replace carries it | Redirect envelope navigates to the target |
|
|
1012
1012
|
|
|
1013
|
-
With `loader(Def, { ssr:
|
|
1013
|
+
With `loader(Def, { ssr: "await" })` the result is settled BEFORE the document
|
|
1014
1014
|
flushes, so both signals are resolved server-side while the tree is built:
|
|
1015
1015
|
`redirect()` replaces the whole page with the redirect carrier (still a 200
|
|
1016
1016
|
document, the client replaces to the target on hydration) and `notFound()`
|
|
@@ -1102,7 +1102,7 @@ on document loads (`metadata.handlesLate`) or progressively on navigations.
|
|
|
1102
1102
|
Handle values land on their own channel and never hold the page, so an effect that needs a late push keys on the handle value, not the URL (see [hooks](../hooks/handle-and-actions.md#when-handle-values-change-by-design)).
|
|
1103
1103
|
A push before your slow fetch usually beats the barrier; a push derived from
|
|
1104
1104
|
the fetched data usually does not. When it MUST be in the document, use
|
|
1105
|
-
`ssr:
|
|
1105
|
+
`ssr: "await"` below. A loader with its own `cache()` keeps its pushes on a hit —
|
|
1106
1106
|
they are replayed from the entry (see "Opting a Loader into Caching"). On a
|
|
1107
1107
|
`ppr` route the race does not apply to a document served from its shell:
|
|
1108
1108
|
there every push a loader makes on that request applies post-hydration (see
|
|
@@ -1115,8 +1115,8 @@ handler reserved with `.defer()` instead (`/breadcrumbs`, "Deferred content").
|
|
|
1115
1115
|
|
|
1116
1116
|
Under `cache()`, loader pushes are not stored with the cached segments: the
|
|
1117
1117
|
loader re-runs on every hit and its live push is the only copy. (`ppr` keeps
|
|
1118
|
-
the pushes of an `ssr:
|
|
1119
|
-
shell, a pushed promise resolved, and a promise-free `ssr:
|
|
1118
|
+
the pushes of an `ssr: "await"` loader, and of the loaders it awaits, in the
|
|
1119
|
+
shell, a pushed promise resolved, and a promise-free `ssr: "await"` loader does
|
|
1120
1120
|
not run on a shell HIT — see `/ppr`.)
|
|
1121
1121
|
|
|
1122
1122
|
Reads are the other direction and gated: `ctx.get(handle)` throws unless the
|
|
@@ -1124,7 +1124,7 @@ loader first does `await ctx.rendered()` (DSL-registered loaders only —
|
|
|
1124
1124
|
handler-invoked loaders cannot use `rendered()`, and a handler already
|
|
1125
1125
|
awaiting the loader via `ctx.use()` makes it a detected deadlock).
|
|
1126
1126
|
|
|
1127
|
-
## `ssr:
|
|
1127
|
+
## `ssr: "await"` — Guarantee a Loader in the Document
|
|
1128
1128
|
|
|
1129
1129
|
Streaming means nothing a slow loader produces is _guaranteed_ in the SSR'd
|
|
1130
1130
|
HTML: its section SSRs as the fallback, a late handle push applies
|
|
@@ -1135,7 +1135,7 @@ use callback:
|
|
|
1135
1135
|
|
|
1136
1136
|
```typescript
|
|
1137
1137
|
path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
1138
|
-
loader(ProductLoader, { ssr:
|
|
1138
|
+
loader(ProductLoader, { ssr: "await" }, () => [cache({ ttl: 60 })]),
|
|
1139
1139
|
loader(RelatedLoader), // untouched: still streams behind its boundary
|
|
1140
1140
|
]),
|
|
1141
1141
|
```
|
|
@@ -1143,9 +1143,14 @@ path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
|
1143
1143
|
The options object is typed as `LoaderOptions`
|
|
1144
1144
|
(`import type { LoaderOptions } from "@rangojs/router"`).
|
|
1145
1145
|
|
|
1146
|
-
The
|
|
1147
|
-
|
|
1148
|
-
|
|
1146
|
+
The value says what the server render does with the loader: `"stream"` (the
|
|
1147
|
+
default, the same as leaving `ssr` out) streams it behind its boundary;
|
|
1148
|
+
`"await"` awaits it in the renders that produce HTML — the document render and
|
|
1149
|
+
a `ppr` shell capture (at runtime, or at build time on a `Prerender` + `ppr`
|
|
1150
|
+
route). (`loading(fallback, { ssr: false })` is a different option: there
|
|
1151
|
+
`false` means the fallback is not server-rendered.)
|
|
1152
|
+
|
|
1153
|
+
Document renders await this loader before first flush — data is settled (`useLoader`
|
|
1149
1154
|
reads it synchronously, no fallback paints), handle pushes beat the barrier
|
|
1150
1155
|
snapshot, and a thrown `notFound()` deterministically precedes Response
|
|
1151
1156
|
construction (real 404, no warm-up race). Client navigations stream exactly as
|
|
@@ -1212,7 +1217,7 @@ path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
|
1212
1217
|
```
|
|
1213
1218
|
|
|
1214
1219
|
(`prefetch` sits beside `ssr` in `LoaderOptions`; combine them as
|
|
1215
|
-
`{ ssr:
|
|
1220
|
+
`{ ssr: "await", prefetch: false }` if you need both.)
|
|
1216
1221
|
|
|
1217
1222
|
| Request | unflagged | `prefetch: false` | `prefetch: "intent"` |
|
|
1218
1223
|
| ------------------------------------------------------------ | ---------- | ------------------------ | ------------------------ |
|
|
@@ -1404,7 +1409,7 @@ The limits:
|
|
|
1404
1409
|
`intercept()` defers nothing. Each intercept loader also runs its own
|
|
1405
1410
|
`revalidate()`, so an action from the modal re-runs only those that say so
|
|
1406
1411
|
(see `/intercept`).
|
|
1407
|
-
- **`ppr` and `ssr:
|
|
1412
|
+
- **`ppr` and `ssr: "await"`.** On a `ppr` route an `ssr: "await"` loader is the
|
|
1408
1413
|
bake lane. A prefetch that replays a shell a document request captured
|
|
1409
1414
|
serves it from its pin: it is not deferred, does not run, and the click
|
|
1410
1415
|
sends nothing. Every other prefetch (a navigation-only shell, a cold page,
|
|
@@ -381,7 +381,7 @@ middleware `ctx` passed in); under `cache()` loaders can still read it because a
|
|
|
381
381
|
DSL loader re-runs on every hit (a `"use cache"` function a loader calls cannot,
|
|
382
382
|
and a loader bound with its own `cache()` only with a `key()`, which switches
|
|
383
383
|
the check off, so the key must include the value). On a `ppr` route only a
|
|
384
|
-
loader without `ssr:
|
|
384
|
+
loader without `ssr: "await"` that no handler awaits may read it: a read the shell
|
|
385
385
|
capture waits for refuses the capture. A middleware `ctx` passed to a
|
|
386
386
|
`"use cache"` function keys the call by host, pathname, params and search, plus
|
|
387
387
|
the route name once the route is matched (a global middleware before `next()`
|
|
@@ -556,7 +556,7 @@ direct visit does. A shell keyed by the visitor's URL
|
|
|
556
556
|
that URL.
|
|
557
557
|
|
|
558
558
|
**Middleware only, by design.** `rewrite()` is a middleware tool. A route
|
|
559
|
-
handler or a loader (any loader, `ssr:
|
|
559
|
+
handler or a loader (any loader, `ssr: "await"` included) cannot rewrite: its
|
|
560
560
|
throw is an error whose message points you to route middleware. A rewrite
|
|
561
561
|
that depends on data goes in the route's middleware, reading the data
|
|
562
562
|
through a cached function the loader shares, so the lookup runs once:
|
|
@@ -40,7 +40,7 @@ Common reasons to migrate:
|
|
|
40
40
|
build-time rendering instead of mixing rendering and caching behind conventions.
|
|
41
41
|
See: `/prerender`
|
|
42
42
|
- **Partial prerendering, shipped** — the `ppr` path option caches a page's
|
|
43
|
-
HTML shell and resumes its holes per request: loaders without `ssr:
|
|
43
|
+
HTML shell and resumes its holes per request: loaders without `ssr: "await"`,
|
|
44
44
|
read under `loading()` or an inline `<Suspense>`. Handler output bakes into
|
|
45
45
|
the shell. It maps to Next's `experimental_ppr`, stable and per-route, but
|
|
46
46
|
holes come from loaders, not from any `<Suspense>`.
|
|
@@ -100,7 +100,7 @@ its call site with the real Rango API:
|
|
|
100
100
|
`next/dynamic`'s `{ ssr: false }` keeps a component out of server rendering:
|
|
101
101
|
render it only after mount in a `"use client"` component (a `useEffect` flag,
|
|
102
102
|
with `React.lazy()` if its code should split out). Do not map it to
|
|
103
|
-
`loader(L, { ssr:
|
|
103
|
+
`loader(L, { ssr: "await" })`, which does the opposite: the server settles that
|
|
104
104
|
loader before the first flush, and under `ppr` it bakes into the shell.
|
|
105
105
|
|
|
106
106
|
If an import has no row here and no obvious Rango equivalent, stop and surface
|
|
@@ -533,7 +533,7 @@ function ProductPage(ctx: HandlerContext) {
|
|
|
533
533
|
);
|
|
534
534
|
}
|
|
535
535
|
|
|
536
|
-
// Rango, live price: a loader without ssr:
|
|
536
|
+
// Rango, live price: a loader without ssr: "await", read with
|
|
537
537
|
// useLoader(LivePriceLoader) in a client component. loading() (or an inline
|
|
538
538
|
// <Suspense> around the reader) is the hole boundary (/ppr → The loader lane
|
|
539
539
|
// rule).
|
|
@@ -560,9 +560,9 @@ Differences that matter during migration:
|
|
|
560
560
|
component, a nested promise in a handle it pushes, a loader it awaits — is
|
|
561
561
|
awaited at capture (bounded by `ppr.captureTimeout`) and served frozen for
|
|
562
562
|
the shell's lifetime; a shell HIT never runs a handler. Keep the parts that
|
|
563
|
-
must be fresh in loaders without `ssr:
|
|
564
|
-
`loading()` or an inline `<Suspense>`; inside an `ssr:
|
|
565
|
-
them as nested promises. For loaders, `ssr:
|
|
563
|
+
must be fresh in loaders without `ssr: "await"`, read with `useLoader` under
|
|
564
|
+
`loading()` or an inline `<Suspense>`; inside an `ssr: "await"` loader, return
|
|
565
|
+
them as nested promises. For loaders, `ssr: "await"` (not `loading()`) selects
|
|
566
566
|
the lane — see `/ppr` → The loader lane rule.
|
|
567
567
|
- **Shell freshness is explicit.** Next's PPR shell is fixed until the next
|
|
568
568
|
build; Rango's has `ttl`/`swr`/`tags` per route, and `updateTag()` /
|
|
@@ -577,7 +577,7 @@ Differences that matter during migration:
|
|
|
577
577
|
`ctx.request.headers`, a `{ cache: false }` variable, and `ctx.dynamic()` refuse it anywhere the
|
|
578
578
|
capture waits: a handler, a promise it passes or pushes, an async component,
|
|
579
579
|
a loader it awaits. Per-user reads must move into a live loader (no
|
|
580
|
-
`ssr:
|
|
580
|
+
`ssr: "await"`; a nested promise does not help). A normal `ctx.get()` value is
|
|
581
581
|
not guarded and bakes as the capturing request's value. The refusal surfaces
|
|
582
582
|
at migration time, which is the point.
|
|
583
583
|
- **A store is required.** PPR needs the app-level `createRouter({ cache })`
|
|
@@ -890,7 +890,7 @@ const HomePage: Handler<"home"> = (ctx) => {
|
|
|
890
890
|
|
|
891
891
|
`generateMetadata({ params })` — DATA-derived, document-blocking metadata —
|
|
892
892
|
maps to a Meta push from the LOADER that owns the data, plus
|
|
893
|
-
`{ ssr:
|
|
893
|
+
`{ ssr: "await" }` for the blocking-until-in-head part:
|
|
894
894
|
|
|
895
895
|
```typescript
|
|
896
896
|
// Next.js: export async function generateMetadata({ params }) {
|
|
@@ -907,7 +907,7 @@ export const ProductLoader = createLoader(async (ctx) => {
|
|
|
907
907
|
});
|
|
908
908
|
|
|
909
909
|
path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
910
|
-
loader(ProductLoader, { ssr:
|
|
910
|
+
loader(ProductLoader, { ssr: "await" }),
|
|
911
911
|
]);
|
|
912
912
|
```
|
|
913
913
|
|
|
@@ -1008,7 +1008,7 @@ See `/theme` for full API including system detection and cookie persistence.
|
|
|
1008
1008
|
10. [ ] Convert loading/error files to `loading()` / `errorBoundary()`
|
|
1009
1009
|
11. [ ] Migrate API routes to `path.json()` / `path.text()`
|
|
1010
1010
|
12. [ ] Update metadata to use `Meta` handle + `<Html.Meta />` in document head
|
|
1011
|
-
(`generateMetadata` → loader push + `{ ssr:
|
|
1011
|
+
(`generateMetadata` → loader push + `{ ssr: "await" }`)
|
|
1012
1012
|
13. [ ] Replace `next-themes` with `theme: true` in createRouter (see `/theme`)
|
|
1013
1013
|
14. [ ] Map rendering-mode segment config: `revalidate = N` → `cache({ ttl })`,
|
|
1014
1014
|
`force-static` → `Static()`/`Prerender()`, `experimental_ppr` → the
|
|
@@ -34,7 +34,7 @@ Common reasons to migrate:
|
|
|
34
34
|
- **Live data layer** — `createLoader()` and `loader()` keep data fresh
|
|
35
35
|
independently of cached UI. A route can serve cached segments while loaders
|
|
36
36
|
still resolve live on every request (on a `ppr` route, a loader registered
|
|
37
|
-
with `ssr:
|
|
37
|
+
with `ssr: "await"` bakes into the cached HTML shell instead; see `/ppr`).
|
|
38
38
|
See: `/loader`, `/caching`, `/cache-guide`
|
|
39
39
|
- **Explicit caching model** — `cache()` DSL, `revalidate()`, `use cache`, and
|
|
40
40
|
custom cache stores make data and render caching a first-class part of the router.
|
|
@@ -153,9 +153,9 @@ const HomePage: Handler<"home"> = (ctx) => {
|
|
|
153
153
|
|
|
154
154
|
RR's data-derived `meta({ data })` maps to the same push from the LOADER that
|
|
155
155
|
owns the data — `ctx.use(Meta)({ title: data.name })` in the loader body, with
|
|
156
|
-
`loader(Def, { ssr:
|
|
156
|
+
`loader(Def, { ssr: "await" })` when the title must be in the SSR'd
|
|
157
157
|
head. See `/loader` → "Writing Handles from Loaders". The loader flag is
|
|
158
|
-
unrelated to RR7's `ssr:
|
|
158
|
+
unrelated to RR7's `ssr: "await"` (SPA mode), which has no route-wide Rango
|
|
159
159
|
equivalent: it makes the server settle that loader before the first flush.
|
|
160
160
|
|
|
161
161
|
Add `<Html.Meta />` in the Document component's `<head>`:
|
|
@@ -48,8 +48,8 @@ Use it only when you need capabilities beyond what the handler provides:
|
|
|
48
48
|
document loads, never an HTTP 302 — pre-stream 302s move to middleware)
|
|
49
49
|
- **`meta({ data })` / `handle` exports** — data-derived page metadata becomes
|
|
50
50
|
a handle push from the loader body (`ctx.use(Meta)({ title: data.name })`),
|
|
51
|
-
with `loader(L, { ssr:
|
|
52
|
-
RR7's `ssr:
|
|
51
|
+
with `loader(L, { ssr: "await" })` when it must be in the SSR'd head (not
|
|
52
|
+
RR7's `ssr: "await"` SPA mode, which has no route-wide Rango equivalent: the
|
|
53
53
|
loader flag makes the server settle that loader before the first flush)
|
|
54
54
|
|
|
55
55
|
If the React Router loader just fetches data for its page component AND the
|
|
@@ -141,10 +141,10 @@ The loader-shaped variant is equally valid — and closer to the RR module when
|
|
|
141
141
|
the loader carried authority. A `createLoader()` body can throw `notFound()`
|
|
142
142
|
for the missing product AND push the data-derived meta itself
|
|
143
143
|
(`ctx.use(Meta)({ title: product.name })`); register it with
|
|
144
|
-
`loader(ProductLoader, { ssr:
|
|
144
|
+
`loader(ProductLoader, { ssr: "await" })` when the 404 status and
|
|
145
145
|
title must be in the document deterministically (the flag makes the server
|
|
146
146
|
settle that loader before the first flush; it is unrelated to RR7's
|
|
147
|
-
`ssr:
|
|
147
|
+
`ssr: "await"` SPA mode, which has no route-wide Rango equivalent). See
|
|
148
148
|
`/loader` → "Loader Authority" and "Writing Handles from Loaders". (One RR
|
|
149
149
|
habit that does NOT carry over: a loader `throw redirect()` is a client-side
|
|
150
150
|
navigate on document loads, never an HTTP 302 — pre-stream 302s belong in
|
package/skills/parallel/SKILL.md
CHANGED
|
@@ -152,10 +152,10 @@ layout, and `Meta` keeps the last value per key, so the override wins.
|
|
|
152
152
|
> just to reach a `ctx.use(Meta)` call: under `cache()` the slot's output is
|
|
153
153
|
> stored with the segment, and on a `ppr` route it bakes into the shell and a
|
|
154
154
|
> HIT replays it without running the slot. The loader can push `Meta` itself
|
|
155
|
-
> (`/loader` → "Writing Handles from Loaders"). Without `ssr:
|
|
155
|
+
> (`/loader` → "Writing Handles from Loaders"). Without `ssr: "await"` the
|
|
156
156
|
> loader runs per request, but on a `ppr` route its push is not in the
|
|
157
157
|
> shell's `<head>` (it applies after hydration). Add
|
|
158
|
-
> `loader(Def, { ssr:
|
|
158
|
+
> `loader(Def, { ssr: "await" })` when the meta must be in the SSR'd head; on a
|
|
159
159
|
> `ppr` route that bakes the push into the shell, and a HIT serves it without
|
|
160
160
|
> running the loader when its value has no promises. Keep the `@meta` slot for
|
|
161
161
|
> metadata that is NOT loader-derived (templates, static descriptions,
|