@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
|
@@ -6,6 +6,13 @@ argument-hint: [path-to-nextjs-app]
|
|
|
6
6
|
|
|
7
7
|
# Migrate from Next.js App Router to @rangojs/router
|
|
8
8
|
|
|
9
|
+
This skill maps a Next.js App Router project onto Rango: project setup, the
|
|
10
|
+
`app/` file conventions to the `urls()` DSL, data fetching and rendering modes
|
|
11
|
+
(SSG, ISR, PPR), middleware, navigation, server actions, metadata, API routes,
|
|
12
|
+
and theming. Use it when porting a Next.js app, or when a Next.js habit needs a
|
|
13
|
+
Rango equivalent. For a side-by-side evaluation rather than a port, see
|
|
14
|
+
`/comparison`; for React Router or Remix apps, see `/migrate-react-router`.
|
|
15
|
+
|
|
9
16
|
## Why Rango
|
|
10
17
|
|
|
11
18
|
Common reasons to migrate:
|
|
@@ -33,8 +40,10 @@ Common reasons to migrate:
|
|
|
33
40
|
build-time rendering instead of mixing rendering and caching behind conventions.
|
|
34
41
|
See: `/prerender`
|
|
35
42
|
- **Partial prerendering, shipped** — the `ppr` path option caches a page's
|
|
36
|
-
HTML shell and resumes
|
|
37
|
-
|
|
43
|
+
HTML shell and resumes its holes per request: loaders without `ssr: false`,
|
|
44
|
+
read under `loading()` or an inline `<Suspense>`. Handler output bakes into
|
|
45
|
+
the shell. It maps to Next's `experimental_ppr`, stable and per-route, but
|
|
46
|
+
holes come from loaders, not from any `<Suspense>`.
|
|
38
47
|
See: `/ppr`
|
|
39
48
|
- **Composable route tree** — layouts, includes, middleware, parallels, and
|
|
40
49
|
intercepts compose directly in the route definition.
|
|
@@ -86,6 +95,13 @@ its call site with the real Rango API:
|
|
|
86
95
|
| `next/font` | see `/fonts` |
|
|
87
96
|
| `next/script` `Script` | see `/scripts` |
|
|
88
97
|
| `next-themes` | `theme: true` in `createRouter` (see §10) |
|
|
98
|
+
| `next/dynamic` `dynamic(() => import(...), { ssr: false })` | render it after mount in a `"use client"` component (see the note below) |
|
|
99
|
+
|
|
100
|
+
`next/dynamic`'s `{ ssr: false }` keeps a component out of server rendering:
|
|
101
|
+
render it only after mount in a `"use client"` component (a `useEffect` flag,
|
|
102
|
+
with `React.lazy()` if its code should split out). Do not map it to
|
|
103
|
+
`loader(L, { ssr: false })`, which does the opposite: the server settles that
|
|
104
|
+
loader before the first flush, and under `ppr` it bakes into the shell.
|
|
89
105
|
|
|
90
106
|
If an import has no row here and no obvious Rango equivalent, stop and surface
|
|
91
107
|
it to the user — do not mock it to keep the build green.
|
|
@@ -99,10 +115,14 @@ Replace Next.js tooling with Vite + Rango:
|
|
|
99
115
|
|
|
100
116
|
```bash
|
|
101
117
|
npm remove next @next/env
|
|
102
|
-
npm install @rangojs/router
|
|
118
|
+
npm install @rangojs/router # keep react and react-dom
|
|
103
119
|
npm install -D vite
|
|
104
120
|
```
|
|
105
121
|
|
|
122
|
+
`rango()` already includes `@vitejs/plugin-rsc` and supplies the client and
|
|
123
|
+
server entries. Add `@vitejs/plugin-react` only if you want Fast Refresh or the
|
|
124
|
+
React Compiler (see `/react-compiler`).
|
|
125
|
+
|
|
106
126
|
```typescript
|
|
107
127
|
// vite.config.ts
|
|
108
128
|
import { defineConfig } from "vite";
|
|
@@ -119,7 +139,7 @@ import { createRouter } from "@rangojs/router";
|
|
|
119
139
|
import { Document } from "./document";
|
|
120
140
|
import { urlpatterns } from "./urls";
|
|
121
141
|
|
|
122
|
-
export
|
|
142
|
+
export const router = createRouter({
|
|
123
143
|
document: Document,
|
|
124
144
|
}).routes(urlpatterns);
|
|
125
145
|
```
|
|
@@ -223,8 +243,8 @@ The `include()` name has three deliberate modes:
|
|
|
223
243
|
| Form | Child route names |
|
|
224
244
|
| ----------------------------------------------- | ----------------------------------------------------------------------- |
|
|
225
245
|
| `include("/", patterns)` | private to the included module; omitted from the app-wide generated map |
|
|
226
|
-
| `include("/", patterns, { name: "marketing" })` | globally registered as `marketing.
|
|
227
|
-
| `include("/", patterns, { name: "" })` | flattened into the parent map as `
|
|
246
|
+
| `include("/", patterns, { name: "marketing" })` | globally registered as `marketing.landing`, `marketing.pricing`, ... |
|
|
247
|
+
| `include("/", patterns, { name: "" })` | flattened into the parent map as `landing`, `pricing`, ... |
|
|
228
248
|
|
|
229
249
|
Use the empty-string form only when the child names are intentionally global
|
|
230
250
|
and unique. Inside a private/namespaced module, prefer dot-local reversal or
|
|
@@ -300,17 +320,23 @@ run the Phase 0 audit before carrying those calls over unchanged.
|
|
|
300
320
|
```typescript
|
|
301
321
|
// Next.js:
|
|
302
322
|
async function ProductPage({ params }) {
|
|
303
|
-
const product = await fetch(
|
|
323
|
+
const product = await fetch(`https://api.example.com/products/${params.slug}`).then(r => r.json());
|
|
304
324
|
return <div>{product.name}</div>;
|
|
305
325
|
}
|
|
306
326
|
|
|
307
327
|
// Rango: same pattern, params come from ctx
|
|
328
|
+
import type { Handler } from "@rangojs/router";
|
|
329
|
+
|
|
308
330
|
const ProductPage: Handler<"product"> = async (ctx) => {
|
|
309
|
-
const product = await fetch(
|
|
331
|
+
const product = await fetch(`https://api.example.com/products/${ctx.params.slug}`).then(r => r.json());
|
|
310
332
|
return <div>{product.name}</div>;
|
|
311
333
|
};
|
|
312
334
|
```
|
|
313
335
|
|
|
336
|
+
If the Next page fetched its own `/api/...` route, call the underlying service
|
|
337
|
+
function directly instead of making a request back to the same app (see
|
|
338
|
+
[backend-host-swap.md](backend-host-swap.md#extract-shared-server-services)).
|
|
339
|
+
|
|
314
340
|
### When to use createLoader
|
|
315
341
|
|
|
316
342
|
Loaders are Rango's live data layer. Use them when you need:
|
|
@@ -360,9 +386,11 @@ export const ProductDef = Prerender<{ slug: string }>(
|
|
|
360
386
|
);
|
|
361
387
|
|
|
362
388
|
// Rango (with live fallback — matches Next.js dynamicParams behavior):
|
|
363
|
-
import { Prerender, Passthrough } from "@rangojs/router";
|
|
389
|
+
import { Prerender, Passthrough, notFound } from "@rangojs/router";
|
|
364
390
|
|
|
365
|
-
|
|
391
|
+
// Keep the definition exported: the Vite plugin injects the stable id only
|
|
392
|
+
// into `export const X = Prerender(...)` (or `const X = ...; export { X }`).
|
|
393
|
+
export const ProductDef = Prerender<{ slug: string }>(
|
|
366
394
|
async () => [{ slug: "a" }, { slug: "b" }],
|
|
367
395
|
async (ctx) => {
|
|
368
396
|
const product = await getProduct(ctx.params.slug);
|
|
@@ -373,8 +401,12 @@ const ProductDef = Prerender<{ slug: string }>(
|
|
|
373
401
|
|
|
374
402
|
export const Product = Passthrough(ProductDef, async (ctx) => {
|
|
375
403
|
const product = await getProduct(ctx.params.slug);
|
|
404
|
+
if (!product) notFound();
|
|
376
405
|
return <ProductPage product={product} />;
|
|
377
406
|
});
|
|
407
|
+
|
|
408
|
+
// In urls:
|
|
409
|
+
path("/products/:slug", Product, { name: "product" });
|
|
378
410
|
```
|
|
379
411
|
|
|
380
412
|
Use `Passthrough()` whenever the Next.js route has `dynamicParams: true` (the
|
|
@@ -395,9 +427,12 @@ Next.js route segment config maps onto Rango's explicit primitives:
|
|
|
395
427
|
### Partial prerendering → the `ppr` path option
|
|
396
428
|
|
|
397
429
|
Next.js PPR statically prerenders a shell at build time and streams the parts
|
|
398
|
-
inside `<Suspense>` at request time. Rango ships
|
|
430
|
+
inside `<Suspense>` at request time. Rango ships a similar model as a path
|
|
399
431
|
option — the shell is captured at runtime into the app cache store and resumed
|
|
400
|
-
on later requests, with the holes rendered fresh per request
|
|
432
|
+
on later requests, with the holes rendered fresh per request. The difference is
|
|
433
|
+
what counts as a hole: in Rango everything a handler produces is shell
|
|
434
|
+
material, as it is under `cache()`, including a promise it hands to a component
|
|
435
|
+
under `<Suspense>`. A live hole is loader data:
|
|
401
436
|
|
|
402
437
|
```typescript
|
|
403
438
|
// Next.js: app/products/[id]/page.tsx
|
|
@@ -412,11 +447,14 @@ export default async function Page({ params }) {
|
|
|
412
447
|
);
|
|
413
448
|
}
|
|
414
449
|
|
|
415
|
-
// Rango
|
|
416
|
-
//
|
|
417
|
-
//
|
|
450
|
+
// Rango: the direct carry-over BAKES. Under ppr the capture awaits a promise
|
|
451
|
+
// the handler hands down (bounded by ppr.captureTimeout), and every shell HIT
|
|
452
|
+
// shows the captured price.
|
|
453
|
+
import { Suspense } from "react";
|
|
454
|
+
import type { HandlerContext } from "@rangojs/router";
|
|
455
|
+
|
|
418
456
|
function ProductPage(ctx: HandlerContext) {
|
|
419
|
-
const price = fetchPrice(ctx.params.id); //
|
|
457
|
+
const price = fetchPrice(ctx.params.id); // awaited at capture, then frozen
|
|
420
458
|
return (
|
|
421
459
|
<ProductShell>
|
|
422
460
|
<Suspense fallback={<PriceSkeleton />}>
|
|
@@ -425,44 +463,54 @@ function ProductPage(ctx: HandlerContext) {
|
|
|
425
463
|
</ProductShell>
|
|
426
464
|
);
|
|
427
465
|
}
|
|
428
|
-
path("/products/:id", ProductPage, {
|
|
429
|
-
name: "product",
|
|
430
|
-
ppr: { ttl: 600, swr: 120 }, // or ppr: true (default ttl 300s)
|
|
431
|
-
});
|
|
432
466
|
|
|
433
|
-
// Rango,
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
//
|
|
467
|
+
// Rango, live price: a loader without ssr: false, read with
|
|
468
|
+
// useLoader(LivePriceLoader) in a client component. loading() (or an inline
|
|
469
|
+
// <Suspense> around the reader) is the hole boundary (/ppr → The loader lane
|
|
470
|
+
// rule).
|
|
471
|
+
function ProductPageLive() {
|
|
472
|
+
return (
|
|
473
|
+
<ProductShell>
|
|
474
|
+
<LivePriceFromLoader /> {/* "use client": useLoader(LivePriceLoader) */}
|
|
475
|
+
</ProductShell>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
437
478
|
path(
|
|
438
479
|
"/products/:id",
|
|
439
|
-
|
|
440
|
-
{ name: "product", ppr: { ttl: 600, swr: 120 } },
|
|
480
|
+
ProductPageLive,
|
|
481
|
+
{ name: "product", ppr: { ttl: 600, swr: 120 } }, // or ppr: true (ttl 300s)
|
|
441
482
|
() => [loader(LivePriceLoader), loading(<PriceSkeleton />)],
|
|
442
483
|
),
|
|
443
484
|
```
|
|
444
485
|
|
|
445
486
|
Differences that matter during migration:
|
|
446
487
|
|
|
447
|
-
- **
|
|
448
|
-
promise
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
the value would bake refuse the capture by construction.
|
|
488
|
+
- **Handler output bakes; loader data is the hole.** Unlike Next, a pending
|
|
489
|
+
promise the handler hands to a component under `<Suspense>` does NOT make a
|
|
490
|
+
hole. Everything a handler produces — that promise, an async server
|
|
491
|
+
component, a nested promise in a handle it pushes, a loader it awaits — is
|
|
492
|
+
awaited at capture (bounded by `ppr.captureTimeout`) and served frozen for
|
|
493
|
+
the shell's lifetime; a shell HIT never runs a handler. Keep the parts that
|
|
494
|
+
must be fresh in loaders without `ssr: false`, read with `useLoader` under
|
|
495
|
+
`loading()` or an inline `<Suspense>`; inside an `ssr: false` loader, return
|
|
496
|
+
them as nested promises. For loaders, `ssr: false` (not `loading()`) selects
|
|
497
|
+
the lane — see `/ppr` → The loader lane rule.
|
|
458
498
|
- **Shell freshness is explicit.** Next's PPR shell is fixed until the next
|
|
459
499
|
build; Rango's has `ttl`/`swr`/`tags` per route, and `updateTag()` /
|
|
460
500
|
`revalidateTag()` drop the shell (`revalidate()` does not — it is a data
|
|
461
|
-
lever and never touches shell HTML).
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
501
|
+
lever and never touches shell HTML). Under a route `cache()`, the shell
|
|
502
|
+
lives no longer than that entry: its ttl/swr are capped by the entry's (dev
|
|
503
|
+
warns when an explicit `ppr` window is reduced). A route `cache({ key })`
|
|
504
|
+
gives one shell per key value, and `cache(false)` or a false `condition()`
|
|
505
|
+
means no shell.
|
|
506
|
+
- **Request-scoped reads in shell material refuse the capture** (in Next they
|
|
507
|
+
silently force dynamic rendering). `cookies()`, `headers()`, a
|
|
508
|
+
`{ cache: false }` variable, and `ctx.dynamic()` refuse it anywhere the
|
|
509
|
+
capture waits: a handler, a promise it passes or pushes, an async component,
|
|
510
|
+
a loader it awaits. Per-user reads must move into a live loader (no
|
|
511
|
+
`ssr: false`; a nested promise does not help). A normal `ctx.get()` value is
|
|
512
|
+
not guarded and bakes as the capturing request's value. The refusal surfaces
|
|
513
|
+
at migration time, which is the point.
|
|
466
514
|
- **A store is required.** PPR needs the app-level `createRouter({ cache })`
|
|
467
515
|
store to implement the shell family (`MemorySegmentCacheStore`,
|
|
468
516
|
`CFCacheStore`, `VercelCacheStore`). Without one the route quietly stays
|
|
@@ -483,20 +531,27 @@ tag-based cache invalidation now maps directly.
|
|
|
483
531
|
with `cache({ tags })` or runtime `cacheTag(...tags)`. `cacheTag()` works inside a
|
|
484
532
|
`"use cache"` function (tags that entry) AND render-callable in a plain server
|
|
485
533
|
component (no `"use cache"` needed — it tags the document / PPR shell the component
|
|
486
|
-
renders into). Then invalidate by tag:
|
|
534
|
+
renders into, and the route `cache()` entry it renders inside). Then invalidate by tag:
|
|
487
535
|
|
|
488
536
|
```typescript
|
|
489
537
|
// Next.js Rango
|
|
490
538
|
// revalidateTag("products") → await updateTag("products") // in a server action: awaitable,
|
|
491
539
|
// // read-your-own-writes (next render is fresh)
|
|
492
540
|
// or revalidateTag("products") // in a route handler / webhook:
|
|
493
|
-
// // background, non-blocking (
|
|
541
|
+
// // background, non-blocking (evicts)
|
|
494
542
|
```
|
|
495
543
|
|
|
496
544
|
`updateTag` is awaitable and immediate; `revalidateTag` is fire-and-forget. Both
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
545
|
+
evict (despite the Next.js name, `revalidateTag` here is NOT
|
|
546
|
+
stale-while-revalidate), and the request that calls either reads its own
|
|
547
|
+
writes. `updateTag()` also waits for the durable write and rejects when it
|
|
548
|
+
fails; `revalidateTag()` runs it in the background. The mutating user's next
|
|
549
|
+
requests skip the stores' per-isolate PPR shell memos via the fresh-reads
|
|
550
|
+
cookie (`rango-state-fresh`); other users, and other locations on
|
|
551
|
+
`CFCacheStore`, see the invalidation once the memo refreshes and KV
|
|
552
|
+
propagates (`/caching` → "The fresh-reads cookie").
|
|
553
|
+
Built-in stores (`MemorySegmentCacheStore`, `CFCacheStore`, `VercelCacheStore`)
|
|
554
|
+
index by tag. Next's
|
|
500
555
|
`revalidatePath` has no path-based equivalent — tag the relevant entries instead.
|
|
501
556
|
|
|
502
557
|
**2. Partial-render selection (which segments re-run after an action).** This is
|
|
@@ -507,7 +562,8 @@ re-rendering:
|
|
|
507
562
|
```typescript
|
|
508
563
|
import { updateBlog } from "./actions/blog";
|
|
509
564
|
|
|
510
|
-
// Re-run this layout when a blog action fires
|
|
565
|
+
// Re-run this layout when a blog action fires (layouts above the route are
|
|
566
|
+
// skipped after actions by default; undefined defers otherwise)
|
|
511
567
|
layout(BlogLayout, () => [
|
|
512
568
|
revalidate((ctx) => ctx.isAction(updateBlog) || undefined),
|
|
513
569
|
path("/blog/:slug", BlogPost, { name: "blogPost" }),
|
|
@@ -617,12 +673,14 @@ path("/dashboard", DashboardPage, { name: "dashboard" }, () => [
|
|
|
617
673
|
"use client";
|
|
618
674
|
export default function Error({ error, reset }) { ... }
|
|
619
675
|
|
|
620
|
-
// Rango: errorBoundary wrapping a group of routes
|
|
676
|
+
// Rango: errorBoundary wrapping a group of routes. The fallback renders on the
|
|
677
|
+
// server and receives only `error` — there is no `reset` (a server render
|
|
678
|
+
// cannot be retried in place; the user navigates away or reloads).
|
|
621
679
|
layout(<DashboardLayout />, () => [
|
|
622
|
-
errorBoundary(({ error
|
|
680
|
+
errorBoundary(({ error }) => (
|
|
623
681
|
<div>
|
|
624
682
|
<h2>Something went wrong</h2>
|
|
625
|
-
<
|
|
683
|
+
<p>{error.message}</p>
|
|
626
684
|
</div>
|
|
627
685
|
)),
|
|
628
686
|
path("/dashboard", DashboardIndex, { name: "dashboard" }),
|
|
@@ -630,6 +688,30 @@ layout(<DashboardLayout />, () => [
|
|
|
630
688
|
])
|
|
631
689
|
```
|
|
632
690
|
|
|
691
|
+
For client-side render errors with a "Try again" button (Next's `reset`), wrap
|
|
692
|
+
the client subtree in `ErrorBoundary` from `@rangojs/router/client`; its
|
|
693
|
+
function fallback receives `{ error, reset }`:
|
|
694
|
+
|
|
695
|
+
```tsx
|
|
696
|
+
"use client";
|
|
697
|
+
import { ErrorBoundary } from "@rangojs/router/client";
|
|
698
|
+
|
|
699
|
+
export function ChartPanel() {
|
|
700
|
+
return (
|
|
701
|
+
<ErrorBoundary
|
|
702
|
+
fallback={({ error, reset }) => (
|
|
703
|
+
<div>
|
|
704
|
+
<p>{error.message}</p>
|
|
705
|
+
<button onClick={reset}>Try again</button>
|
|
706
|
+
</div>
|
|
707
|
+
)}
|
|
708
|
+
>
|
|
709
|
+
<Chart />
|
|
710
|
+
</ErrorBoundary>
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
633
715
|
```typescript
|
|
634
716
|
// Next.js: app/not-found.tsx
|
|
635
717
|
export default function NotFound() { ... }
|
|
@@ -657,15 +739,17 @@ children in their scope — handlers, loaders, and nested segments.
|
|
|
657
739
|
|
|
658
740
|
## 6. Navigation
|
|
659
741
|
|
|
660
|
-
| Next.js
|
|
661
|
-
|
|
|
662
|
-
| `import Link from "next/link"`
|
|
663
|
-
| `<Link href="/about">`
|
|
664
|
-
| `useRouter().push("/about")`
|
|
665
|
-
| `useRouter().replace("/about")`
|
|
666
|
-
| `
|
|
667
|
-
| `
|
|
668
|
-
| `
|
|
742
|
+
| Next.js | Rango |
|
|
743
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
744
|
+
| `import Link from "next/link"` | `import { Link } from "@rangojs/router/client"` |
|
|
745
|
+
| `<Link href="/about">` | `<Link to="/about">` |
|
|
746
|
+
| `useRouter().push("/about")` | `useRouter().push("/about")` |
|
|
747
|
+
| `useRouter().replace("/about")` | `useRouter().replace("/about")` |
|
|
748
|
+
| `useRouter().back()` / `refresh()` / `prefetch(url)` | same methods on `useRouter()` from `@rangojs/router/client` |
|
|
749
|
+
| `useParams()` | `useParams()` from `@rangojs/router/client` (or `ctx.params` in a server handler) |
|
|
750
|
+
| `usePathname()` | `usePathname()` from `@rangojs/router/client` |
|
|
751
|
+
| `useSearchParams()` | `useSearchParams()` from `@rangojs/router/client` — returns an RR-style TUPLE, so destructure the reader: `const [searchParams] = useSearchParams()`; the second element is a setter Next does not have |
|
|
752
|
+
| `redirect("/login")` (server) | `redirect("/login")` from `@rangojs/router` |
|
|
669
753
|
|
|
670
754
|
### "Instant navigations" (Link prefetching)
|
|
671
755
|
|
|
@@ -699,22 +783,22 @@ has. Server-component routes and `clientUrls()` groups compose in one tree.
|
|
|
699
783
|
|
|
700
784
|
## 7. Server Actions
|
|
701
785
|
|
|
702
|
-
Server actions work the same way — `"use server"` directive, `useActionState`, form actions. No migration needed for action logic.
|
|
786
|
+
Server actions work the same way — `"use server"` directive, `useActionState`, form actions. No migration needed for action logic. Keep passing the imported action itself to `<form action={...}>` or `useActionState(action, initial)` (not a client-side closure around it) so the form still submits with JavaScript disabled.
|
|
703
787
|
|
|
704
788
|
Key difference: in Rango, route middleware does NOT wrap action execution. Actions only see global middleware context. Use `getRequestContext()` in actions to access `ctx.set()`/`ctx.get()`.
|
|
705
789
|
|
|
706
|
-
Next.js's `revalidateTag()` maps directly: tag entries via `cache({ tags })` / `cacheTag(...)`, then invalidate. **In a server action use `await updateTag(tag)`** — it is read-your-own-writes, so the action's own re-render sees fresh data; `revalidateTag(tag)` is a background (non-blocking) hard-purge
|
|
790
|
+
Next.js's `revalidateTag()` maps directly: tag entries via `cache({ tags })` / `cacheTag(...)`, then invalidate. **In a server action use `await updateTag(tag)`** — it is read-your-own-writes, so the action's own re-render sees fresh data, and it rejects when the store's durable write fails; `revalidateTag(tag)` is a background (non-blocking) hard-purge for route handlers / webhooks. The request that calls `revalidateTag()` also reads its own writes, but a failed durable write only reaches `onError`. `revalidatePath()` has no path-based equivalent — tag the route's entries instead. Separately, the route, the segments inside its `path()`, and all loaders already re-run after every action; layouts and parallels above the route are skipped. To re-render one of those skipped segments after an action, attach `revalidate((ctx) => ctx.isAction(updateBlog) || undefined)` to it; to narrow a loader or route segment to one action, use `revalidate((ctx) => (ctx.isAction() ? ctx.isAction(updateBlog) : undefined))` (match the imported action by reference, as in §3). See `/server-actions` for the full pattern (validation, error handling, file uploads), `/caching` for tag invalidation, and `/loader` for revalidation rule semantics.
|
|
707
791
|
|
|
708
792
|
## 8. Metadata / Head
|
|
709
793
|
|
|
710
|
-
Rango uses the `Meta` handle + `<
|
|
794
|
+
Rango uses the `Meta` handle + `<Html.Meta />` client component:
|
|
711
795
|
|
|
712
796
|
```typescript
|
|
713
797
|
// Next.js: export const metadata = { title: "Home" }
|
|
714
798
|
// Next.js: export function generateMetadata({ params }) { ... }
|
|
715
799
|
|
|
716
|
-
// Rango: Meta handle in handlers (server),
|
|
717
|
-
import { Meta } from "@rangojs/router";
|
|
800
|
+
// Rango: Meta handle in handlers (server), Html.Meta in document <head> (client)
|
|
801
|
+
import { Meta, type Handler } from "@rangojs/router";
|
|
718
802
|
|
|
719
803
|
const HomePage: Handler<"home"> = (ctx) => {
|
|
720
804
|
const meta = ctx.use(Meta);
|
|
@@ -751,16 +835,16 @@ Without the flag the push still applies, but a slow loader's title lands
|
|
|
751
835
|
post-hydration instead of in the document — see `/loader` → "Writing Handles
|
|
752
836
|
from Loaders" for the delivery race.
|
|
753
837
|
|
|
754
|
-
Add `<
|
|
838
|
+
Add `<Html.Meta />` in the Document component's `<head>`:
|
|
755
839
|
|
|
756
840
|
```typescript
|
|
757
|
-
import {
|
|
841
|
+
import { Html } from "@rangojs/router/client";
|
|
758
842
|
|
|
759
843
|
function Document({ children }: { children: ReactNode }) {
|
|
760
844
|
return (
|
|
761
845
|
<html>
|
|
762
846
|
<head>
|
|
763
|
-
<
|
|
847
|
+
<Html.Meta />
|
|
764
848
|
</head>
|
|
765
849
|
<body>{children}</body>
|
|
766
850
|
</html>
|
|
@@ -835,13 +919,15 @@ See `/theme` for full API including system detection and cookie persistence.
|
|
|
835
919
|
4. [ ] Create `router.tsx` with `createRouter()`
|
|
836
920
|
5. [ ] Convert file-based routes to `urls()` DSL in `urls.tsx`
|
|
837
921
|
6. [ ] Migrate layouts to `layout()` with `<Outlet />`
|
|
838
|
-
7. [ ]
|
|
922
|
+
7. [ ] Keep inline server `fetch`/DB calls in handlers; add `createLoader()` +
|
|
923
|
+
`loader()` only where you need live, refreshable, or independently
|
|
924
|
+
cached data (§3)
|
|
839
925
|
8. [ ] Migrate `middleware.ts` to `router.use()` (auth, guards, logging)
|
|
840
926
|
9. [ ] Replace `next/link` with `Link` from `@rangojs/router/client`; keep
|
|
841
927
|
"instant navigations" via `prefetch="viewport"`/`defaultPrefetch` (§6)
|
|
842
928
|
10. [ ] Convert loading/error files to `loading()` / `errorBoundary()`
|
|
843
929
|
11. [ ] Migrate API routes to `path.json()` / `path.text()`
|
|
844
|
-
12. [ ] Update metadata to use `Meta` handle + `<
|
|
930
|
+
12. [ ] Update metadata to use `Meta` handle + `<Html.Meta />` in document head
|
|
845
931
|
(`generateMetadata` → loader push + `{ ssr: false }`)
|
|
846
932
|
13. [ ] Replace `next-themes` with `theme: true` in createRouter (see `/theme`)
|
|
847
933
|
14. [ ] Map rendering-mode segment config: `revalidate = N` → `cache({ ttl })`,
|
|
@@ -55,7 +55,9 @@ actor and tenant scope at the repository boundary, verify membership/role, and
|
|
|
55
55
|
perform the data query through that scope. Avoid exporting an unrestricted DB
|
|
56
56
|
handle to page handlers.
|
|
57
57
|
|
|
58
|
-
Tests must prove denial, not only success
|
|
58
|
+
Tests must prove denial, not only success (the `/testing` skill's `runMiddleware`,
|
|
59
|
+
`runLoader`, and `runInRequestContext` take your DB double through `env`; see its
|
|
60
|
+
`bindings.md`):
|
|
59
61
|
|
|
60
62
|
- a user cannot read another tenant by guessing an ID;
|
|
61
63
|
- a member cannot perform an owner/admin mutation;
|
|
@@ -102,7 +104,8 @@ For a Node-to-Workers move, search for:
|
|
|
102
104
|
|
|
103
105
|
Prefer web standards (`fetch`, `Request`, `Response`, WebCrypto) and provider
|
|
104
106
|
bindings. Test the production bundle in `vite preview`; a successful TypeScript
|
|
105
|
-
check does not prove an SDK runs in workerd.
|
|
107
|
+
check does not prove an SDK runs in workerd. `/cloudflare` covers bindings,
|
|
108
|
+
local D1, streaming, and deploying the built Worker.
|
|
106
109
|
|
|
107
110
|
## Plan cutover and rollback
|
|
108
111
|
|
|
@@ -6,7 +6,12 @@ argument-hint: path-to-react-router-app
|
|
|
6
6
|
|
|
7
7
|
# Migrate from React Router to @rangojs/router
|
|
8
8
|
|
|
9
|
-
Covers React Router v7 (framework mode and library mode), v6, and Remix v2
|
|
9
|
+
Covers React Router v7 (framework mode and library mode), v6, and Remix v2:
|
|
10
|
+
choosing a target shape per route group, replacing imports, route mapping,
|
|
11
|
+
loaders and actions, middleware, error/loading states, navigation, metadata,
|
|
12
|
+
resource routes, theming, and Workers deployment. Use it when porting one of
|
|
13
|
+
those apps to Rango. For Next.js apps see `/migrate-nextjs`; for a feature
|
|
14
|
+
comparison rather than a port see `/comparison`.
|
|
10
15
|
|
|
11
16
|
## Why Rango
|
|
12
17
|
|
|
@@ -28,7 +33,8 @@ Common reasons to migrate:
|
|
|
28
33
|
See: `/middleware`, `/loader`
|
|
29
34
|
- **Live data layer** — `createLoader()` and `loader()` keep data fresh
|
|
30
35
|
independently of cached UI. A route can serve cached segments while loaders
|
|
31
|
-
still resolve live on every request
|
|
36
|
+
still resolve live on every request (on a `ppr` route, a loader registered
|
|
37
|
+
with `ssr: false` bakes into the cached HTML shell instead; see `/ppr`).
|
|
32
38
|
See: `/loader`, `/caching`, `/cache-guide`
|
|
33
39
|
- **Explicit caching model** — `cache()` DSL, `revalidate()`, `use cache`, and
|
|
34
40
|
custom cache stores make data and render caching a first-class part of the router.
|
|
@@ -109,19 +115,19 @@ unsupported behavior until runtime, and keep the old packages in the dependency
|
|
|
109
115
|
graph. Replace every `react-router` / `react-router-dom` / `@remix-run/*`
|
|
110
116
|
import at its call site:
|
|
111
117
|
|
|
112
|
-
| React Router import | Replace with
|
|
113
|
-
| --------------------------------------------- |
|
|
114
|
-
| `Link`, `NavLink` | `Link` from `@rangojs/router/client` (`NavLink` active state via `usePathname()`, see §6)
|
|
115
|
-
| `Outlet` | `Outlet` from `@rangojs/router/client`
|
|
116
|
-
| `useNavigate` | `useRouter()` from `@rangojs/router/client` (see §6)
|
|
117
|
-
| `useLocation`, `useSearchParams`, `useParams` | `usePathname()`, `useSearchParams()` (same `[params, setParams]` tuple), `useParams()` from `@rangojs/router/client` |
|
|
118
|
-
| `useLoaderData` | merge the loader into the handler; `useLoader()` for live client data or `clientUrls()` routes (see §3)
|
|
119
|
-
| `useActionData` | `useActionState` (standard React, see §3)
|
|
120
|
-
| `Form` | `<form action={serverAction}>` with a `"use server"` function (see §3)
|
|
121
|
-
| `useFetcher` | submits → server actions + `useActionState`/`useOptimistic`; reads → `useLoader()`
|
|
122
|
-
| `defer` / `Await` | `loading()` DSL / plain `<Suspense>` (see §5)
|
|
123
|
-
| `json()`, `redirect()` | plain return values; `redirect` from `@rangojs/router`
|
|
124
|
-
| `useRouteError` | the `error` prop of `errorBoundary()` (see §5)
|
|
118
|
+
| React Router import | Replace with |
|
|
119
|
+
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
120
|
+
| `Link`, `NavLink` | `Link` from `@rangojs/router/client` (`NavLink` active state via `usePathname()`, see §6) |
|
|
121
|
+
| `Outlet` | `Outlet` from `@rangojs/router/client` |
|
|
122
|
+
| `useNavigate` | `useRouter()` from `@rangojs/router/client` (see §6) |
|
|
123
|
+
| `useLocation`, `useSearchParams`, `useParams` | `usePathname()`, `useSearchParams()` (same `[params, setParams]` tuple), `useParams()` from `@rangojs/router/client`; `location.state` → typed `useLocationState()` (see `/hooks`) |
|
|
124
|
+
| `useLoaderData` | merge the loader into the handler; `useLoader()` for live client data or `clientUrls()` routes (see §3) |
|
|
125
|
+
| `useActionData` | `useActionState` (standard React, see §3) |
|
|
126
|
+
| `Form` | `<form action={serverAction}>` with a `"use server"` function (see §3) |
|
|
127
|
+
| `useFetcher` | submits → server actions + `useActionState`/`useOptimistic`; reads → `useLoader()` / `useFetchLoader()` (see `/hooks`) |
|
|
128
|
+
| `defer` / `Await` | `loading()` DSL / plain `<Suspense>` (see §5) |
|
|
129
|
+
| `json()`, `redirect()` | plain return values; `redirect` from `@rangojs/router` |
|
|
130
|
+
| `useRouteError` | the `error` prop of `errorBoundary()` (see §5) |
|
|
125
131
|
|
|
126
132
|
If an import has no row here and no obvious Rango equivalent, stop and surface
|
|
127
133
|
it to the user — do not mock it to keep the build green.
|
|
@@ -142,48 +148,54 @@ Each numbered step's full walkthrough lives in a companion file linked below.
|
|
|
142
148
|
|
|
143
149
|
## 11. Key Conceptual Differences
|
|
144
150
|
|
|
145
|
-
| Concept | React Router
|
|
146
|
-
| ------------------- |
|
|
147
|
-
| Rendering | Client-side by default, SSR opt-in
|
|
148
|
-
| Data loading | `loader()` + `useLoaderData()`
|
|
149
|
-
| Form actions | Route-scoped `action()`
|
|
150
|
-
| Route definition | File-based or `createBrowserRouter`
|
|
151
|
-
| Middleware |
|
|
152
|
-
| Parallel routes | Not built-in
|
|
153
|
-
| Intercepting routes | Not built-in
|
|
154
|
-
| Caching | Not built-in
|
|
155
|
-
| Type-safe routes | Partial (v7 framework mode)
|
|
151
|
+
| Concept | React Router | Rango |
|
|
152
|
+
| ------------------- | --------------------------------------------------------------------------- | -------------------------------------- |
|
|
153
|
+
| Rendering | Client-side by default, SSR opt-in | Server components by default (RSC) |
|
|
154
|
+
| Data loading | `loader()` + `useLoaderData()` | Direct fetch in server components |
|
|
155
|
+
| Form actions | Route-scoped `action()` | Function-scoped `"use server"` |
|
|
156
|
+
| Route definition | File-based or `createBrowserRouter` | `urls()` DSL with `path()`, `layout()` |
|
|
157
|
+
| Middleware | v7: route `middleware` export; v6 / Remix v2: none (guards live in loaders) | `router.use()` + DSL `middleware()` |
|
|
158
|
+
| Parallel routes | Not built-in | `parallel()` DSL |
|
|
159
|
+
| Intercepting routes | Not built-in | `intercept()` DSL |
|
|
160
|
+
| Caching | Not built-in | `cache()` DSL, `"use cache"` |
|
|
161
|
+
| Type-safe routes | Partial (v7 framework mode) | Full: params, names, href, reverse |
|
|
156
162
|
|
|
157
163
|
## Migration Checklist
|
|
158
164
|
|
|
159
165
|
1. [ ] Set up Vite config with `rango()` plugin
|
|
160
|
-
2. [ ] Create Document component with `<
|
|
166
|
+
2. [ ] Create Document component with `<Html.Meta />` in head
|
|
161
167
|
3. [ ] Create `router.tsx` with `createRouter()`
|
|
162
168
|
4. [ ] Convert route config / file routes to `urls()` DSL
|
|
163
169
|
5. [ ] Migrate layouts — keep `<Outlet />` (import from `@rangojs/router/client`)
|
|
164
|
-
6. [ ] Merge loaders + components into handler functions (fetch + render in one place)
|
|
170
|
+
6. [ ] Merge loaders + components into handler functions (fetch + render in one place),
|
|
171
|
+
or port hook-heavy groups with `clientUrls()` (see "Two target shapes")
|
|
165
172
|
7. [ ] Convert React Router actions to `"use server"` functions
|
|
166
173
|
8. [ ] Migrate auth guards from loaders to `router.use()`
|
|
167
174
|
9. [ ] Replace `react-router-dom` Link/navigation with `@rangojs/router/client`
|
|
168
175
|
10. [ ] Convert error boundaries to `errorBoundary()` DSL
|
|
169
|
-
11. [ ] Update metadata to use `Meta` handle + `<
|
|
176
|
+
11. [ ] Update metadata to use `Meta` handle + `<Html.Meta />`
|
|
170
177
|
12. [ ] Replace custom theme provider with `theme: true` in createRouter (see `/theme`)
|
|
171
178
|
13. [ ] Run `npx rango generate src/` to generate route types
|
|
172
179
|
14. [ ] Verify no shims: `grep -rnE "from ['\"](react-router|@remix-run)" src/ app/`
|
|
173
180
|
returns nothing, no mock modules or aliases exist, and the packages are
|
|
174
181
|
out of `package.json`
|
|
175
182
|
|
|
176
|
-
**Cloudflare Workers (if migrating an RR7-on-Workers app):**
|
|
183
|
+
**Cloudflare Workers (if migrating an RR7-on-Workers app):** the linked
|
|
184
|
+
sections are in [`../cloudflare/references/streaming-and-deploy.md`](../cloudflare/references/streaming-and-deploy.md).
|
|
177
185
|
|
|
178
|
-
|
|
186
|
+
15. [ ] Audit the custom worker entry — the `router.fetch()` response must pass
|
|
179
187
|
through as a **stream** (`new Response(response.body, response)`); remove
|
|
180
188
|
any `.text()`/`.arrayBuffer()`/`HTMLRewriter` buffering from the Rango
|
|
181
|
-
path (keep it only on legacy/proxy branches). See
|
|
182
|
-
|
|
189
|
+
path (keep it only on legacy/proxy branches). See
|
|
190
|
+
[Keep streamed responses intact](../cloudflare/references/streaming-and-deploy.md#keep-streamed-responses-intact).
|
|
191
|
+
16. [ ] Switch local dev to `vite dev` / `vite preview` (they stream + load
|
|
183
192
|
`.dev.vars` + provide bindings); stop using `wrangler dev` for local
|
|
184
|
-
verification —
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
193
|
+
verification — its local gzip can buffer and hide streaming. See
|
|
194
|
+
[Use Vite for local RSC work](../cloudflare/references/streaming-and-deploy.md#use-vite-for-local-rsc-work).
|
|
195
|
+
17. [ ] Move build output paths from RR7's `build/` to vite's `dist/`: client
|
|
196
|
+
assets land in `dist/client/`; update cleanup scripts to match.
|
|
197
|
+
18. [ ] Deploy with the generated config (`wrangler deploy -c dist/rsc/wrangler.json`,
|
|
198
|
+
or `dist/<name>/wrangler.json` for a custom RSC environment name), not a
|
|
199
|
+
root `wrangler deploy` against the source worker entry (Wrangler cannot
|
|
200
|
+
rebuild Rango's Vite virtual modules). See
|
|
201
|
+
[Deploy the built Worker](../cloudflare/references/streaming-and-deploy.md#deploy-the-built-worker).
|