@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100
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/AGENTS.md +9 -0
- package/README.md +1037 -4
- package/dist/bin/rango.js +1619 -157
- package/dist/vite/index.js +5762 -2301
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +71 -63
- package/skills/breadcrumbs/SKILL.md +252 -0
- package/skills/cache-guide/SKILL.md +294 -0
- package/skills/caching/SKILL.md +93 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +367 -71
- package/skills/host-router/SKILL.md +218 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +176 -8
- package/skills/layout/SKILL.md +124 -3
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +474 -47
- package/skills/middleware/SKILL.md +207 -37
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +15 -11
- package/skills/parallel/SKILL.md +272 -1
- package/skills/prerender/SKILL.md +467 -65
- package/skills/rango/SKILL.md +89 -21
- package/skills/response-routes/SKILL.md +152 -91
- package/skills/route/SKILL.md +305 -14
- package/skills/router-setup/SKILL.md +210 -32
- package/skills/server-actions/SKILL.md +739 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +333 -86
- package/skills/use-cache/SKILL.md +324 -0
- package/skills/view-transitions/SKILL.md +212 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +136 -68
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +374 -561
- package/src/browser/navigation-client.ts +228 -70
- package/src/browser/navigation-store.ts +97 -55
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +376 -315
- package/src/browser/prefetch/cache.ts +314 -0
- package/src/browser/prefetch/fetch.ts +282 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +191 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +152 -0
- package/src/browser/react/Link.tsx +255 -71
- package/src/browser/react/NavigationProvider.tsx +152 -24
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +55 -0
- package/src/browser/react/index.ts +15 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +30 -120
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +44 -65
- package/src/browser/react/use-params.ts +78 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +99 -0
- package/src/browser/react/use-router.ts +83 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +85 -99
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +246 -64
- package/src/browser/scroll-restoration.ts +127 -52
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +510 -603
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +158 -48
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +84 -23
- package/src/build/generate-route-types.ts +39 -828
- package/src/build/index.ts +4 -5
- package/src/build/route-trie.ts +85 -32
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +418 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +618 -0
- package/src/build/route-types/scan-filter.ts +85 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +342 -0
- package/src/cache/cache-scope.ts +167 -307
- package/src/cache/cf/cf-cache-store.ts +573 -21
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +6 -1
- package/src/client.tsx +118 -302
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +156 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +55 -10
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +138 -21
- package/src/index.ts +206 -51
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +25 -143
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +159 -13
- package/src/prerender.ts +397 -29
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +231 -121
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +1134 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +483 -0
- package/src/route-definition/index.ts +55 -0
- package/src/route-definition/redirect.ts +101 -0
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition.ts +1 -1431
- package/src/route-map-builder.ts +162 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +66 -9
- package/src/router/content-negotiation.ts +215 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +418 -86
- package/src/router/intercept-resolution.ts +35 -20
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +359 -128
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +98 -32
- package/src/router/match-api.ts +196 -261
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +441 -0
- package/src/router/match-middleware/background-revalidation.ts +108 -93
- package/src/router/match-middleware/cache-lookup.ts +415 -86
- package/src/router/match-middleware/cache-store.ts +91 -29
- package/src/router/match-middleware/intercept-resolution.ts +48 -21
- package/src/router/match-middleware/segment-resolution.ts +73 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +154 -35
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +209 -0
- package/src/router/middleware.ts +373 -371
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +292 -52
- package/src/router/prerender-match.ts +502 -0
- package/src/router/preview-match.ts +98 -0
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +152 -39
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +41 -21
- package/src/router/router-interfaces.ts +484 -0
- package/src/router/router-options.ts +618 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +756 -0
- package/src/router/segment-resolution/helpers.ts +268 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1407 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1315
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +111 -39
- package/src/router/types.ts +17 -9
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +642 -2011
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +864 -1114
- package/src/rsc/helpers.ts +181 -19
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +229 -0
- package/src/rsc/manifest-init.ts +90 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +395 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +360 -0
- package/src/rsc/rsc-rendering.ts +256 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +360 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +52 -11
- package/src/search-params.ts +230 -0
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +187 -38
- package/src/server/context.ts +333 -59
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +113 -15
- package/src/server/loader-registry.ts +24 -64
- package/src/server/request-context.ts +603 -109
- package/src/server.ts +35 -155
- package/src/ssr/index.tsx +107 -30
- package/src/static-handler.ts +126 -0
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +764 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +209 -0
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +120 -0
- package/src/types/segments.ts +167 -0
- package/src/types.ts +1 -1757
- package/src/urls/include-helper.ts +207 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +372 -0
- package/src/urls/path-helper.ts +364 -0
- package/src/urls/pattern-types.ts +107 -0
- package/src/urls/response-types.ts +108 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1282
- package/src/use-loader.tsx +161 -81
- package/src/vite/debug.ts +184 -0
- package/src/vite/discovery/bundle-postprocess.ts +181 -0
- package/src/vite/discovery/discover-routers.ts +376 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +486 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +117 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +15 -2063
- package/src/vite/plugin-types.ts +103 -0
- package/src/vite/plugins/cjs-to-esm.ts +98 -0
- package/src/vite/plugins/client-ref-dedup.ts +131 -0
- package/src/vite/plugins/client-ref-hashing.ts +117 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
- package/src/vite/plugins/expose-id-utils.ts +299 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +816 -0
- package/src/vite/plugins/performance-tracks.ts +96 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +336 -0
- package/src/vite/plugins/version-injector.ts +109 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +497 -0
- package/src/vite/router-discovery.ts +1423 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +161 -0
- package/src/vite/utils/prerender-utils.ts +222 -0
- package/src/vite/utils/shared-utils.ts +170 -0
- package/CLAUDE.md +0 -43
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- package/src/vite/expose-prerender-handler-id.ts +0 -429
- package/src/vite/package-resolution.ts +0 -125
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/skills/hooks/SKILL.md
CHANGED
|
@@ -6,49 +6,85 @@ argument-hint: [hook-name]
|
|
|
6
6
|
|
|
7
7
|
# Client-Side React Hooks
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Import the hooks and components in this skill from `@rangojs/router/client`.
|
|
10
|
+
The root `@rangojs/router` entrypoint is for server/RSC APIs and shared types.
|
|
10
11
|
|
|
11
12
|
## Navigation Hooks
|
|
12
13
|
|
|
13
14
|
### useNavigation()
|
|
14
15
|
|
|
15
|
-
Track navigation state
|
|
16
|
+
Track reactive navigation state (state-only, no actions):
|
|
16
17
|
|
|
17
18
|
```tsx
|
|
18
19
|
"use client";
|
|
19
|
-
import { useNavigation } from "@rangojs/router";
|
|
20
|
+
import { useNavigation } from "@rangojs/router/client";
|
|
20
21
|
|
|
21
22
|
function NavIndicator() {
|
|
22
23
|
const nav = useNavigation();
|
|
23
24
|
|
|
24
|
-
//
|
|
25
|
-
nav.state;
|
|
26
|
-
nav.isStreaming;
|
|
27
|
-
nav.location;
|
|
28
|
-
nav.pendingUrl;
|
|
25
|
+
// State properties
|
|
26
|
+
nav.state; // 'idle' | 'loading'
|
|
27
|
+
nav.isStreaming; // boolean
|
|
28
|
+
nav.location; // Current URL
|
|
29
|
+
nav.pendingUrl; // Target URL during navigation (or null)
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
nav.navigate("/products"); // Navigate programmatically
|
|
32
|
-
nav.navigate("/products", { replace: true }); // Replace history
|
|
33
|
-
nav.refresh(); // Refresh current route
|
|
34
|
-
|
|
35
|
-
return nav.state === 'loading' ? <Spinner /> : null;
|
|
31
|
+
return nav.state === "loading" ? <Spinner /> : null;
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
// With selector for performance
|
|
34
|
+
// With selector for performance (re-renders only when selected value changes)
|
|
39
35
|
function IsLoading() {
|
|
40
|
-
const isLoading = useNavigation(nav => nav.state ===
|
|
36
|
+
const isLoading = useNavigation((nav) => nav.state === "loading");
|
|
41
37
|
return isLoading ? <Spinner /> : null;
|
|
42
38
|
}
|
|
43
39
|
```
|
|
44
40
|
|
|
41
|
+
### useRouter()
|
|
42
|
+
|
|
43
|
+
Access stable router actions (never causes re-renders):
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
"use client";
|
|
47
|
+
import { useRouter } from "@rangojs/router/client";
|
|
48
|
+
|
|
49
|
+
function NavigationControls() {
|
|
50
|
+
const router = useRouter();
|
|
51
|
+
|
|
52
|
+
router.push("/products"); // Navigate (adds history entry)
|
|
53
|
+
router.replace("/login"); // Navigate (replaces history entry)
|
|
54
|
+
router.refresh(); // Re-fetch current route data
|
|
55
|
+
router.prefetch("/dashboard"); // Prefetch for faster navigation
|
|
56
|
+
router.back(); // Go back in history
|
|
57
|
+
router.forward(); // Go forward in history
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
#### Skipping revalidation
|
|
62
|
+
|
|
63
|
+
Pass `revalidate: false` to skip the RSC server fetch for same-pathname navigations (search param or hash changes). The URL updates and all hooks re-render, but server components stay as-is.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
// Update search params without server round-trip
|
|
67
|
+
router.push("/products?color=blue", { revalidate: false });
|
|
68
|
+
router.replace("/products?page=3", { revalidate: false });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If the pathname changes, `revalidate: false` is silently ignored and a full navigation occurs. This also works on `<Link>`:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<Link to="/products?color=blue" revalidate={false}>
|
|
75
|
+
Blue
|
|
76
|
+
</Link>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Plain `<a>` tags can opt in via `data-revalidate="false"`.
|
|
80
|
+
|
|
45
81
|
### useSegments()
|
|
46
82
|
|
|
47
83
|
Access current URL path and matched route segments:
|
|
48
84
|
|
|
49
85
|
```tsx
|
|
50
86
|
"use client";
|
|
51
|
-
import { useSegments } from "@rangojs/router";
|
|
87
|
+
import { useSegments } from "@rangojs/router/client";
|
|
52
88
|
|
|
53
89
|
function Breadcrumbs() {
|
|
54
90
|
const { path, segmentIds, location } = useSegments();
|
|
@@ -61,7 +97,7 @@ function Breadcrumbs() {
|
|
|
61
97
|
}
|
|
62
98
|
|
|
63
99
|
// With selector
|
|
64
|
-
const isShopRoute = useSegments(s => s.path[0] === "shop");
|
|
100
|
+
const isShopRoute = useSegments((s) => s.path[0] === "shop");
|
|
65
101
|
```
|
|
66
102
|
|
|
67
103
|
### useLinkStatus()
|
|
@@ -81,7 +117,7 @@ function LoadingIndicator() {
|
|
|
81
117
|
<Link to="/dashboard">
|
|
82
118
|
Dashboard
|
|
83
119
|
<LoadingIndicator />
|
|
84
|
-
</Link
|
|
120
|
+
</Link>;
|
|
85
121
|
```
|
|
86
122
|
|
|
87
123
|
## Data Hooks
|
|
@@ -92,7 +128,7 @@ Access loader data (strict - data guaranteed):
|
|
|
92
128
|
|
|
93
129
|
```tsx
|
|
94
130
|
"use client";
|
|
95
|
-
import { useLoader } from "@rangojs/router";
|
|
131
|
+
import { useLoader } from "@rangojs/router/client";
|
|
96
132
|
import { ProductLoader } from "../loaders/product";
|
|
97
133
|
|
|
98
134
|
function ProductPrice() {
|
|
@@ -128,7 +164,7 @@ Access loader with on-demand fetching (flexible):
|
|
|
128
164
|
|
|
129
165
|
```tsx
|
|
130
166
|
"use client";
|
|
131
|
-
import { useFetchLoader } from "@rangojs/router";
|
|
167
|
+
import { useFetchLoader } from "@rangojs/router/client";
|
|
132
168
|
import { SearchLoader } from "../loaders/search";
|
|
133
169
|
|
|
134
170
|
function SearchResults() {
|
|
@@ -146,37 +182,83 @@ function SearchResults() {
|
|
|
146
182
|
<div>
|
|
147
183
|
<input onChange={(e) => handleSearch(e.target.value)} />
|
|
148
184
|
{isLoading && <Spinner />}
|
|
149
|
-
{data?.results.map(r =>
|
|
185
|
+
{data?.results.map((r) => (
|
|
186
|
+
<Result key={r.id} {...r} />
|
|
187
|
+
))}
|
|
150
188
|
</div>
|
|
151
189
|
);
|
|
152
190
|
}
|
|
153
191
|
```
|
|
154
192
|
|
|
155
193
|
**Load options**:
|
|
194
|
+
|
|
156
195
|
```tsx
|
|
196
|
+
// JSON body — sent as application/json, available as ctx.body on the server
|
|
157
197
|
await load({
|
|
158
|
-
method:
|
|
159
|
-
params: { query:
|
|
160
|
-
body: { data:
|
|
198
|
+
method: "POST",
|
|
199
|
+
params: { query: "test" },
|
|
200
|
+
body: { data: "value" },
|
|
161
201
|
});
|
|
202
|
+
|
|
203
|
+
// FormData body — sent as multipart/form-data, available as ctx.formData on the server.
|
|
204
|
+
// Automatically detected: when body is a FormData instance, the request switches
|
|
205
|
+
// to multipart/form-data to preserve File objects and binary data.
|
|
206
|
+
const formData = new FormData();
|
|
207
|
+
formData.append("file", fileInput.files[0]);
|
|
208
|
+
await load({ method: "POST", body: formData });
|
|
162
209
|
```
|
|
163
210
|
|
|
164
|
-
|
|
211
|
+
**Body type auto-switching**: The `load()` function inspects the `body` value to
|
|
212
|
+
choose the encoding. If `body instanceof FormData`, the request is sent as
|
|
213
|
+
`multipart/form-data` (browser sets the boundary header automatically). Otherwise
|
|
214
|
+
the body is JSON-serialized and sent with `Content-Type: application/json`. On the
|
|
215
|
+
server, JSON bodies are available via `ctx.body` and FormData bodies via `ctx.formData`.
|
|
165
216
|
|
|
166
|
-
|
|
217
|
+
**File upload example**:
|
|
167
218
|
|
|
168
219
|
```tsx
|
|
169
220
|
"use client";
|
|
170
|
-
import {
|
|
221
|
+
import { useFetchLoader } from "@rangojs/router/client";
|
|
222
|
+
import { FileUploadLoader } from "../loaders/upload";
|
|
171
223
|
|
|
172
|
-
function
|
|
173
|
-
const
|
|
174
|
-
|
|
224
|
+
function FileUploader() {
|
|
225
|
+
const { data, load, isLoading } = useFetchLoader(FileUploadLoader);
|
|
226
|
+
const formRef = useRef<HTMLFormElement>(null);
|
|
227
|
+
|
|
228
|
+
const handleSubmit = async (formData: FormData) => {
|
|
229
|
+
await load({ method: "POST", body: formData });
|
|
230
|
+
formRef.current?.reset();
|
|
231
|
+
};
|
|
175
232
|
|
|
176
|
-
return
|
|
233
|
+
return (
|
|
234
|
+
<form ref={formRef} action={handleSubmit}>
|
|
235
|
+
<input type="file" name="file" />
|
|
236
|
+
<button type="submit" disabled={isLoading}>
|
|
237
|
+
{isLoading ? "Uploading..." : "Upload"}
|
|
238
|
+
</button>
|
|
239
|
+
{data?.uploadedFile && <p>Uploaded: {data.uploadedFile.name}</p>}
|
|
240
|
+
</form>
|
|
241
|
+
);
|
|
177
242
|
}
|
|
178
243
|
```
|
|
179
244
|
|
|
245
|
+
Server-side loader for the upload:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { createLoader } from "@rangojs/router";
|
|
249
|
+
|
|
250
|
+
export const FileUploadLoader = createLoader(async (ctx) => {
|
|
251
|
+
"use server";
|
|
252
|
+
|
|
253
|
+
const file = ctx.formData?.get("file") as File | null;
|
|
254
|
+
if (file && file.size > 0) {
|
|
255
|
+
// Process file (save to R2, D1, etc.)
|
|
256
|
+
return { uploadedFile: { name: file.name, size: file.size } };
|
|
257
|
+
}
|
|
258
|
+
return { uploadedFile: null };
|
|
259
|
+
}, true); // true = fetchable (can be called from the client via load())
|
|
260
|
+
```
|
|
261
|
+
|
|
180
262
|
## Handle Hooks
|
|
181
263
|
|
|
182
264
|
### useHandle()
|
|
@@ -185,8 +267,7 @@ Access accumulated handle data from route segments:
|
|
|
185
267
|
|
|
186
268
|
```tsx
|
|
187
269
|
"use client";
|
|
188
|
-
import { useHandle } from "@rangojs/router";
|
|
189
|
-
import { Breadcrumbs } from "../handles/breadcrumbs";
|
|
270
|
+
import { useHandle, Breadcrumbs } from "@rangojs/router/client";
|
|
190
271
|
|
|
191
272
|
function BreadcrumbNav() {
|
|
192
273
|
const crumbs = useHandle(Breadcrumbs);
|
|
@@ -205,7 +286,7 @@ function BreadcrumbNav() {
|
|
|
205
286
|
}
|
|
206
287
|
|
|
207
288
|
// With selector
|
|
208
|
-
const lastCrumb = useHandle(Breadcrumbs, data => data.at(-1));
|
|
289
|
+
const lastCrumb = useHandle(Breadcrumbs, (data) => data.at(-1));
|
|
209
290
|
```
|
|
210
291
|
|
|
211
292
|
Handles can be passed as props from server to client components:
|
|
@@ -216,16 +297,23 @@ path("/dashboard", (ctx) => {
|
|
|
216
297
|
const push = ctx.use(Breadcrumbs);
|
|
217
298
|
push({ label: "Dashboard", href: "/dashboard" });
|
|
218
299
|
return <DashboardNav handle={Breadcrumbs} />;
|
|
219
|
-
})
|
|
300
|
+
});
|
|
301
|
+
```
|
|
220
302
|
|
|
303
|
+
```tsx
|
|
221
304
|
// Client component — typeof infers the full Handle<T> type
|
|
222
305
|
"use client";
|
|
223
|
-
import { useHandle } from "@rangojs/router/client";
|
|
224
|
-
import type { Breadcrumbs } from "../handles";
|
|
306
|
+
import { useHandle, type Breadcrumbs } from "@rangojs/router/client";
|
|
225
307
|
|
|
226
308
|
function DashboardNav({ handle }: { handle: typeof Breadcrumbs }) {
|
|
227
309
|
const crumbs = useHandle(handle);
|
|
228
|
-
return
|
|
310
|
+
return (
|
|
311
|
+
<nav>
|
|
312
|
+
{crumbs.map((c) => (
|
|
313
|
+
<a href={c.href}>{c.label}</a>
|
|
314
|
+
))}
|
|
315
|
+
</nav>
|
|
316
|
+
);
|
|
229
317
|
}
|
|
230
318
|
```
|
|
231
319
|
|
|
@@ -235,13 +323,18 @@ RSC serialization strips the `collect` function via `toJSON()`. On the client,
|
|
|
235
323
|
|
|
236
324
|
## Action Hooks
|
|
237
325
|
|
|
326
|
+
For the full server-action guide (defining actions, `useActionState`,
|
|
327
|
+
`useOptimistic`, validation, revalidation, error handling, file uploads),
|
|
328
|
+
see `/server-actions`. `useAction()` below is a Rango-specific hook for
|
|
329
|
+
tracking actions called outside a `<form action={...}>` flow.
|
|
330
|
+
|
|
238
331
|
### useAction()
|
|
239
332
|
|
|
240
333
|
Track state of server action invocations:
|
|
241
334
|
|
|
242
335
|
```tsx
|
|
243
336
|
"use client";
|
|
244
|
-
import { useAction } from "@rangojs/router";
|
|
337
|
+
import { useAction } from "@rangojs/router/client";
|
|
245
338
|
import { addToCart } from "../actions/cart";
|
|
246
339
|
|
|
247
340
|
function AddToCartButton({ productId }: { productId: string }) {
|
|
@@ -256,8 +349,8 @@ function AddToCartButton({ productId }: { productId: string }) {
|
|
|
256
349
|
return (
|
|
257
350
|
<form action={addToCart}>
|
|
258
351
|
<input type="hidden" name="productId" value={productId} />
|
|
259
|
-
<button disabled={state ===
|
|
260
|
-
{state ===
|
|
352
|
+
<button disabled={state === "loading"}>
|
|
353
|
+
{state === "loading" ? "Adding..." : "Add to Cart"}
|
|
261
354
|
</button>
|
|
262
355
|
{error && <p className="error">{error.message}</p>}
|
|
263
356
|
</form>
|
|
@@ -265,7 +358,7 @@ function AddToCartButton({ productId }: { productId: string }) {
|
|
|
265
358
|
}
|
|
266
359
|
|
|
267
360
|
// Match by string suffix (convenient but may be ambiguous)
|
|
268
|
-
const isLoading = useAction(
|
|
361
|
+
const isLoading = useAction("addToCart", (s) => s.state === "loading");
|
|
269
362
|
```
|
|
270
363
|
|
|
271
364
|
## State Hooks
|
|
@@ -276,20 +369,29 @@ Read type-safe state from history:
|
|
|
276
369
|
|
|
277
370
|
```tsx
|
|
278
371
|
"use client";
|
|
279
|
-
import { useLocationState, createLocationState } from "@rangojs/router";
|
|
372
|
+
import { useLocationState, createLocationState } from "@rangojs/router/client";
|
|
280
373
|
|
|
281
|
-
// Define typed state
|
|
374
|
+
// Define typed state (all export patterns supported)
|
|
375
|
+
// Keys are auto-injected by the Vite plugin -- no manual key needed.
|
|
282
376
|
export const ProductState = createLocationState<{
|
|
283
377
|
name: string;
|
|
284
378
|
price: number;
|
|
285
379
|
}>();
|
|
286
380
|
|
|
381
|
+
// Also valid: const ProductState = createLocationState<...>();
|
|
382
|
+
// export { ProductState };
|
|
383
|
+
// Also valid: export { ProductState as MyState };
|
|
384
|
+
|
|
287
385
|
function ProductHeader() {
|
|
288
386
|
const state = useLocationState(ProductState);
|
|
289
387
|
// { name: string; price: number } | undefined
|
|
290
388
|
|
|
291
389
|
if (state) {
|
|
292
|
-
return
|
|
390
|
+
return (
|
|
391
|
+
<h1>
|
|
392
|
+
{state.name} - ${state.price}
|
|
393
|
+
</h1>
|
|
394
|
+
);
|
|
293
395
|
}
|
|
294
396
|
return <h1>Loading...</h1>;
|
|
295
397
|
}
|
|
@@ -301,14 +403,114 @@ Pass state through Link:
|
|
|
301
403
|
import { Link } from "@rangojs/router/client";
|
|
302
404
|
import { ProductState } from "./state";
|
|
303
405
|
|
|
406
|
+
<Link to="/product/123" state={[ProductState({ name: "Widget", price: 99 })]}>
|
|
407
|
+
View Product
|
|
408
|
+
</Link>;
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Pass typed state just in time (getter evaluated at click time, not render time):
|
|
412
|
+
|
|
413
|
+
```tsx
|
|
414
|
+
"use client"; // JIT state requires a client component (getter can't cross RSC boundary)
|
|
415
|
+
|
|
416
|
+
import { Link } from "@rangojs/router/client";
|
|
417
|
+
import { ProductState } from "./state";
|
|
418
|
+
|
|
419
|
+
// The getter is stored lazily and only called when the user clicks the link.
|
|
420
|
+
// This is useful for capturing values that change after render (e.g., scroll
|
|
421
|
+
// position, form state, ref values).
|
|
304
422
|
<Link
|
|
305
423
|
to="/product/123"
|
|
306
|
-
state={[ProductState({ name:
|
|
424
|
+
state={[ProductState(() => ({ name: product.name, price: product.price }))]}
|
|
307
425
|
>
|
|
308
426
|
View Product
|
|
427
|
+
</Link>;
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Plain state can also be evaluated just in time (also requires a client component):
|
|
431
|
+
|
|
432
|
+
```tsx
|
|
433
|
+
<Link to="/product/123" state={() => ({ from: window.location.pathname })}>
|
|
434
|
+
View Product
|
|
309
435
|
</Link>
|
|
310
436
|
```
|
|
311
437
|
|
|
438
|
+
### Flash State (read-once)
|
|
439
|
+
|
|
440
|
+
Create a location state with `{ flash: true }` for read-once state that
|
|
441
|
+
auto-clears after first render. Ideal for flash messages (success/error
|
|
442
|
+
notifications after redirect):
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
// location-states.ts
|
|
446
|
+
import { createLocationState } from "@rangojs/router";
|
|
447
|
+
|
|
448
|
+
export const FlashMessage = createLocationState<{ text: string }>({
|
|
449
|
+
flash: true,
|
|
450
|
+
});
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Read flash state with `useLocationState` (same hook as persistent state):
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
"use client";
|
|
457
|
+
import { useLocationState } from "@rangojs/router/client";
|
|
458
|
+
import { FlashMessage } from "../location-states";
|
|
459
|
+
|
|
460
|
+
function FlashBanner() {
|
|
461
|
+
const flash = useLocationState(FlashMessage);
|
|
462
|
+
// { text: string } | undefined
|
|
463
|
+
|
|
464
|
+
if (!flash) return null;
|
|
465
|
+
return <div className="flash">{flash.text}</div>;
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Flash behavior is determined by the definition (`{ flash: true }`), not by which
|
|
470
|
+
hook reads it. `useLocationState` reads the value synchronously during render,
|
|
471
|
+
then clears it from `history.state` via `replaceState` in a `useEffect`.
|
|
472
|
+
Multiple components reading the same flash definition all see the value.
|
|
473
|
+
Pressing back/forward will not re-show the flash since it was cleared.
|
|
474
|
+
|
|
475
|
+
Set flash state from the server via `redirect()` with state:
|
|
476
|
+
|
|
477
|
+
```tsx
|
|
478
|
+
// In a route handler
|
|
479
|
+
import { redirect, createLocationState } from "@rangojs/router";
|
|
480
|
+
|
|
481
|
+
export const FlashMessage = createLocationState<{ text: string }>({
|
|
482
|
+
flash: true,
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Handler
|
|
486
|
+
(ctx) => {
|
|
487
|
+
return redirect("/dashboard", {
|
|
488
|
+
state: [FlashMessage({ text: "Item saved!" })],
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Or via `ctx.setLocationState()` on any response:
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
(ctx) => {
|
|
497
|
+
ctx.setLocationState(FlashMessage({ text: "Welcome back!" }));
|
|
498
|
+
return <Dashboard />;
|
|
499
|
+
};
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### .read() (non-hook access)
|
|
503
|
+
|
|
504
|
+
Read current location state outside React components (client-side only):
|
|
505
|
+
|
|
506
|
+
```tsx
|
|
507
|
+
import { FlashMessage, ProductState } from "../location-states";
|
|
508
|
+
|
|
509
|
+
// Returns TState | undefined. Returns undefined during SSR.
|
|
510
|
+
const flash = FlashMessage.read();
|
|
511
|
+
const product = ProductState.read();
|
|
512
|
+
```
|
|
513
|
+
|
|
312
514
|
## Cache Hooks
|
|
313
515
|
|
|
314
516
|
### useClientCache()
|
|
@@ -317,15 +519,15 @@ Manually control client-side navigation cache:
|
|
|
317
519
|
|
|
318
520
|
```tsx
|
|
319
521
|
"use client";
|
|
320
|
-
import { useClientCache } from "@rangojs/router";
|
|
522
|
+
import { useClientCache } from "@rangojs/router/client";
|
|
321
523
|
|
|
322
524
|
function SaveButton() {
|
|
323
525
|
const { clear } = useClientCache();
|
|
324
526
|
|
|
325
527
|
const handleSave = async () => {
|
|
326
|
-
await fetch(
|
|
327
|
-
method:
|
|
328
|
-
body: JSON.stringify(data)
|
|
528
|
+
await fetch("/api/data", {
|
|
529
|
+
method: "POST",
|
|
530
|
+
body: JSON.stringify(data),
|
|
329
531
|
});
|
|
330
532
|
|
|
331
533
|
// Invalidate cache after mutation
|
|
@@ -345,7 +547,7 @@ function SaveButton() {
|
|
|
345
547
|
Render child content in layouts:
|
|
346
548
|
|
|
347
549
|
```tsx
|
|
348
|
-
import { Outlet, ParallelOutlet } from "@rangojs/router";
|
|
550
|
+
import { Outlet, ParallelOutlet } from "@rangojs/router/client";
|
|
349
551
|
|
|
350
552
|
function DashboardLayout({ children }: { children?: React.ReactNode }) {
|
|
351
553
|
return (
|
|
@@ -353,9 +555,7 @@ function DashboardLayout({ children }: { children?: React.ReactNode }) {
|
|
|
353
555
|
<aside>
|
|
354
556
|
<ParallelOutlet name="@sidebar" />
|
|
355
557
|
</aside>
|
|
356
|
-
<main>
|
|
357
|
-
{children ?? <Outlet />}
|
|
358
|
-
</main>
|
|
558
|
+
<main>{children ?? <Outlet />}</main>
|
|
359
559
|
<ParallelOutlet name="@notifications" />
|
|
360
560
|
</div>
|
|
361
561
|
);
|
|
@@ -368,7 +568,7 @@ Access outlet content programmatically:
|
|
|
368
568
|
|
|
369
569
|
```tsx
|
|
370
570
|
"use client";
|
|
371
|
-
import { useOutlet } from "@rangojs/router";
|
|
571
|
+
import { useOutlet } from "@rangojs/router/client";
|
|
372
572
|
|
|
373
573
|
function ConditionalLayout() {
|
|
374
574
|
const outlet = useOutlet();
|
|
@@ -384,6 +584,78 @@ function ConditionalLayout() {
|
|
|
384
584
|
|
|
385
585
|
## URL Hooks
|
|
386
586
|
|
|
587
|
+
### useParams()
|
|
588
|
+
|
|
589
|
+
Access route params from the current URL:
|
|
590
|
+
|
|
591
|
+
```tsx
|
|
592
|
+
"use client";
|
|
593
|
+
import { useParams } from "@rangojs/router/client";
|
|
594
|
+
|
|
595
|
+
// Route: /product/:productId
|
|
596
|
+
function ProductPage() {
|
|
597
|
+
const params = useParams();
|
|
598
|
+
// { productId: "123" }
|
|
599
|
+
|
|
600
|
+
return <h1>Product {params.productId}</h1>;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Annotate the expected shape via a generic
|
|
604
|
+
function ProductPageTyped() {
|
|
605
|
+
const { productId } = useParams<{ productId: string }>();
|
|
606
|
+
return <h1>Product {productId}</h1>;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// With selector for performance (re-renders only when selected value changes)
|
|
610
|
+
function ProductId() {
|
|
611
|
+
const productId = useParams((p) => p.productId);
|
|
612
|
+
return <span>ID: {productId}</span>;
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Returns merged params from all matched route segments as a `Readonly<T>` map. Updates on navigation commit (not during pending navigation).
|
|
617
|
+
|
|
618
|
+
### usePathname()
|
|
619
|
+
|
|
620
|
+
Access the current URL pathname:
|
|
621
|
+
|
|
622
|
+
```tsx
|
|
623
|
+
"use client";
|
|
624
|
+
import { usePathname } from "@rangojs/router/client";
|
|
625
|
+
|
|
626
|
+
function CurrentPage() {
|
|
627
|
+
const pathname = usePathname();
|
|
628
|
+
// "/product/123" (no search params)
|
|
629
|
+
|
|
630
|
+
return <span>Current path: {pathname}</span>;
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
Returns the pathname string without search params or hash. Updates on navigation commit.
|
|
635
|
+
|
|
636
|
+
### useSearchParams()
|
|
637
|
+
|
|
638
|
+
Access the current URL search params:
|
|
639
|
+
|
|
640
|
+
```tsx
|
|
641
|
+
"use client";
|
|
642
|
+
import { useSearchParams } from "@rangojs/router/client";
|
|
643
|
+
|
|
644
|
+
function SearchResults() {
|
|
645
|
+
const searchParams = useSearchParams();
|
|
646
|
+
const query = searchParams.get("q"); // "react"
|
|
647
|
+
const page = searchParams.get("page"); // "2"
|
|
648
|
+
|
|
649
|
+
return (
|
|
650
|
+
<div>
|
|
651
|
+
Searching for: {query}, page {page}
|
|
652
|
+
</div>
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
Returns a `ReadonlyURLSearchParams` (URLSearchParams without mutation methods). During SSR, returns empty params and syncs from the browser URL on mount.
|
|
658
|
+
|
|
387
659
|
### useHref()
|
|
388
660
|
|
|
389
661
|
Mount-aware href for client components inside `include()` scopes:
|
|
@@ -422,21 +694,45 @@ function MountInfo() {
|
|
|
422
694
|
}
|
|
423
695
|
```
|
|
424
696
|
|
|
425
|
-
|
|
697
|
+
### useReverse(routes)
|
|
698
|
+
|
|
699
|
+
Mount-aware local reverse for client components. Import the generated `routes` map from a `urls()` module's `.gen.ts` and call `reverse(".name", params?)`. Auto-fills params from `useParams()`; explicit params override.
|
|
700
|
+
|
|
701
|
+
```tsx
|
|
702
|
+
"use client";
|
|
703
|
+
import { Link, useReverse } from "@rangojs/router/client";
|
|
704
|
+
import { routes as blogRoutes } from "../urls/blog.gen.js";
|
|
705
|
+
|
|
706
|
+
function BlogNav() {
|
|
707
|
+
const reverse = useReverse(blogRoutes);
|
|
708
|
+
return (
|
|
709
|
+
<nav>
|
|
710
|
+
<Link to={reverse(".index")}>Blog</Link>
|
|
711
|
+
<Link to={reverse(".post", { postId: "hello" })}>Post</Link>
|
|
712
|
+
</nav>
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
See `/links` for the full URL generation guide. `ctx.reverse()` is server-only; on the client, prefer `useReverse(routes)` for in-module names and pass URLs as props for cross-module ones.
|
|
426
718
|
|
|
427
719
|
## Hook Summary
|
|
428
720
|
|
|
429
|
-
| Hook
|
|
430
|
-
|
|
431
|
-
| `
|
|
432
|
-
| `
|
|
433
|
-
| `
|
|
434
|
-
| `
|
|
435
|
-
| `
|
|
436
|
-
| `
|
|
437
|
-
| `
|
|
438
|
-
| `
|
|
439
|
-
| `
|
|
440
|
-
| `
|
|
441
|
-
| `
|
|
442
|
-
| `
|
|
721
|
+
| Hook | Purpose | Returns |
|
|
722
|
+
| -------------------- | --------------------------------- | ------------------------------------------------------------------ |
|
|
723
|
+
| `useParams()` | Route params | `Readonly<T>` (default `Record<string, string>`) or selected value |
|
|
724
|
+
| `usePathname()` | Current pathname | `string` |
|
|
725
|
+
| `useSearchParams()` | URL search params | `ReadonlyURLSearchParams` |
|
|
726
|
+
| `useHref()` | Mount-aware href | `(path) => string` |
|
|
727
|
+
| `useMount()` | Current include() mount path | `string` |
|
|
728
|
+
| `useReverse()` | Local reverse for imported routes | `(name, params?, search?) => string` |
|
|
729
|
+
| `useNavigation()` | Reactive navigation state | state, location, isStreaming |
|
|
730
|
+
| `useRouter()` | Stable router actions | push, replace, refresh, prefetch, back, forward |
|
|
731
|
+
| `useSegments()` | URL path & segment IDs | path, segmentIds, location |
|
|
732
|
+
| `useLinkStatus()` | Link pending state | { pending } |
|
|
733
|
+
| `useLoader()` | Loader data (strict) | data, isLoading, error |
|
|
734
|
+
| `useFetchLoader()` | Loader with on-demand fetch | data, load, isLoading |
|
|
735
|
+
| `useHandle()` | Accumulated handle data | T (handle type) |
|
|
736
|
+
| `useAction()` | Server action state | state, error, result |
|
|
737
|
+
| `useLocationState()` | History state (persists or flash) | T \| undefined |
|
|
738
|
+
| `useClientCache()` | Cache control | { clear } |
|