react-router 8.3.1 → 8.5.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/CHANGELOG.md +83 -0
- package/dist/development/dom-export.d.ts +2 -1
- package/dist/development/dom-export.js +1 -1
- package/dist/development/index-react-server-client.js +2 -2
- package/dist/development/index-react-server.d.ts +601 -189
- package/dist/development/index-react-server.js +100 -48
- package/dist/development/index.d.ts +10 -9
- package/dist/development/index.js +6 -5
- package/dist/development/lib/actions.js +1 -1
- package/dist/development/lib/components.js +66 -10
- package/dist/development/lib/context.d.ts +19 -6
- package/dist/development/lib/context.js +11 -3
- package/dist/development/lib/dom/dom.d.ts +3 -0
- package/dist/development/lib/dom/dom.js +1 -1
- package/dist/development/lib/dom/global.d.ts +35 -0
- package/dist/development/lib/dom/lib.d.ts +34 -9
- package/dist/development/lib/dom/lib.js +30 -37
- package/dist/development/lib/dom/server.d.ts +6 -1
- package/dist/development/lib/dom/server.js +41 -15
- package/dist/development/lib/dom/ssr/components.js +26 -31
- package/dist/development/lib/dom/ssr/data.js +1 -1
- package/dist/development/lib/dom/ssr/entry.d.ts +13 -2
- package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/development/lib/dom/ssr/fallback.js +1 -1
- package/dist/development/lib/dom/ssr/fog-of-war.d.ts +3 -2
- package/dist/development/lib/dom/ssr/fog-of-war.js +46 -22
- package/dist/development/lib/dom/ssr/hydration.d.ts +3 -1
- package/dist/development/lib/dom/ssr/hydration.js +6 -4
- package/dist/development/lib/dom/ssr/invariant.js +1 -1
- package/dist/development/lib/dom/ssr/links.js +1 -1
- package/dist/development/lib/dom/ssr/markup.js +5 -2
- package/dist/development/lib/dom/ssr/route-discovery.d.ts +120 -0
- package/dist/development/lib/dom/ssr/route-discovery.js +415 -0
- package/dist/development/lib/dom/ssr/routeModules.js +1 -1
- package/dist/development/lib/dom/ssr/routes-test-stub.js +2 -2
- package/dist/development/lib/dom/ssr/routes.d.ts +2 -2
- package/dist/development/lib/dom/ssr/routes.js +8 -8
- package/dist/development/lib/dom/ssr/server.js +4 -2
- package/dist/development/lib/dom/ssr/single-fetch.d.ts +28 -2
- package/dist/development/lib/dom/ssr/single-fetch.js +80 -71
- package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/development/lib/dom-export/hydrated-router.d.ts +28 -0
- package/dist/development/lib/dom-export/hydrated-router.js +33 -12
- package/dist/development/lib/errors.js +1 -1
- package/dist/development/lib/hooks.d.ts +3 -3
- package/dist/development/lib/hooks.js +52 -47
- package/dist/development/lib/href.js +1 -1
- package/dist/development/lib/router/history.js +1 -1
- package/dist/development/lib/router/instrumentation.js +1 -1
- package/dist/development/lib/router/matcher-route-pattern.d.ts +2 -0
- package/dist/development/lib/router/matcher-route-pattern.js +216 -0
- package/dist/development/lib/router/matcher-route-pattern.preload.d.ts +20 -0
- package/dist/development/lib/router/matcher-route-pattern.preload.js +36 -0
- package/dist/development/lib/router/matcher.d.ts +2 -0
- package/dist/development/lib/router/matcher.js +29 -0
- package/dist/development/lib/router/navigation.js +1 -1
- package/dist/development/lib/router/router.d.ts +19 -7
- package/dist/development/lib/router/router.js +235 -128
- package/dist/development/lib/router/url.js +1 -1
- package/dist/development/lib/router/utils.d.ts +30 -5
- package/dist/development/lib/router/utils.js +8 -5
- package/dist/development/lib/rsc/browser.js +3 -2
- package/dist/development/lib/rsc/errorBoundaries.js +1 -1
- package/dist/development/lib/rsc/html-stream/browser.js +1 -1
- package/dist/development/lib/rsc/html-stream/server.js +25 -15
- package/dist/development/lib/rsc/route-modules.js +1 -1
- package/dist/development/lib/rsc/server.rsc.d.ts +10 -2
- package/dist/development/lib/rsc/server.ssr.js +25 -21
- package/dist/development/lib/server-runtime/build.d.ts +14 -0
- package/dist/development/lib/server-runtime/cookies.js +1 -1
- package/dist/development/lib/server-runtime/crypto.js +1 -1
- package/dist/development/lib/server-runtime/data.js +1 -1
- package/dist/development/lib/server-runtime/dev.js +1 -1
- package/dist/development/lib/server-runtime/entry.js +1 -1
- package/dist/development/lib/server-runtime/errors.js +1 -1
- package/dist/development/lib/server-runtime/headers.js +1 -1
- package/dist/development/lib/server-runtime/invariant.js +1 -1
- package/dist/development/lib/server-runtime/mode.js +1 -1
- package/dist/development/lib/server-runtime/routeMatching.js +3 -4
- package/dist/development/lib/server-runtime/routes.d.ts +1 -1
- package/dist/development/lib/server-runtime/routes.js +1 -1
- package/dist/development/lib/server-runtime/server.js +18 -9
- package/dist/development/lib/server-runtime/serverHandoff.d.ts +17 -0
- package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions.js +1 -1
- package/dist/development/lib/server-runtime/single-fetch.js +1 -1
- package/dist/development/lib/server-runtime/urls.js +1 -1
- package/dist/development/lib/server-runtime/warnings.js +1 -1
- package/dist/development/lib/types/internal.js +1 -1
- package/dist/development/lib/types/route-data.d.ts +2 -2
- package/dist/development/lib/types/route-module-annotations.d.ts +1 -1
- package/dist/development/route-pattern.d.ts +3 -0
- package/dist/development/route-pattern.js +12 -0
- package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
- package/dist/production/dom-export.d.ts +2 -1
- package/dist/production/dom-export.js +1 -1
- package/dist/production/index-react-server-client.js +2 -2
- package/dist/production/index-react-server.d.ts +601 -189
- package/dist/production/index-react-server.js +100 -48
- package/dist/production/index.d.ts +10 -9
- package/dist/production/index.js +6 -5
- package/dist/production/lib/actions.js +1 -1
- package/dist/production/lib/components.js +66 -10
- package/dist/production/lib/context.d.ts +19 -6
- package/dist/production/lib/context.js +11 -3
- package/dist/production/lib/dom/dom.d.ts +3 -0
- package/dist/production/lib/dom/dom.js +1 -1
- package/dist/production/lib/dom/global.d.ts +35 -0
- package/dist/production/lib/dom/lib.d.ts +34 -9
- package/dist/production/lib/dom/lib.js +30 -37
- package/dist/production/lib/dom/server.d.ts +6 -1
- package/dist/production/lib/dom/server.js +41 -15
- package/dist/production/lib/dom/ssr/components.js +26 -31
- package/dist/production/lib/dom/ssr/data.js +1 -1
- package/dist/production/lib/dom/ssr/entry.d.ts +13 -2
- package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/production/lib/dom/ssr/fallback.js +1 -1
- package/dist/production/lib/dom/ssr/fog-of-war.d.ts +3 -2
- package/dist/production/lib/dom/ssr/fog-of-war.js +46 -22
- package/dist/production/lib/dom/ssr/hydration.d.ts +3 -1
- package/dist/production/lib/dom/ssr/hydration.js +6 -4
- package/dist/production/lib/dom/ssr/invariant.js +1 -1
- package/dist/production/lib/dom/ssr/links.js +1 -1
- package/dist/production/lib/dom/ssr/markup.js +5 -2
- package/dist/production/lib/dom/ssr/route-discovery.d.ts +120 -0
- package/dist/production/lib/dom/ssr/route-discovery.js +415 -0
- package/dist/production/lib/dom/ssr/routeModules.js +1 -1
- package/dist/production/lib/dom/ssr/routes-test-stub.js +2 -2
- package/dist/production/lib/dom/ssr/routes.d.ts +2 -2
- package/dist/production/lib/dom/ssr/routes.js +8 -8
- package/dist/production/lib/dom/ssr/server.js +4 -2
- package/dist/production/lib/dom/ssr/single-fetch.d.ts +28 -2
- package/dist/production/lib/dom/ssr/single-fetch.js +80 -71
- package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/production/lib/dom-export/hydrated-router.d.ts +28 -0
- package/dist/production/lib/dom-export/hydrated-router.js +33 -12
- package/dist/production/lib/errors.js +1 -1
- package/dist/production/lib/hooks.d.ts +3 -3
- package/dist/production/lib/hooks.js +51 -46
- package/dist/production/lib/href.js +1 -1
- package/dist/production/lib/router/history.js +1 -1
- package/dist/production/lib/router/instrumentation.js +1 -1
- package/dist/production/lib/router/matcher-route-pattern.d.ts +2 -0
- package/dist/production/lib/router/matcher-route-pattern.js +216 -0
- package/dist/production/lib/router/matcher-route-pattern.preload.d.ts +20 -0
- package/dist/production/lib/router/matcher-route-pattern.preload.js +36 -0
- package/dist/production/lib/router/matcher.d.ts +2 -0
- package/dist/production/lib/router/matcher.js +29 -0
- package/dist/production/lib/router/navigation.js +1 -1
- package/dist/production/lib/router/router.d.ts +19 -7
- package/dist/production/lib/router/router.js +235 -128
- package/dist/production/lib/router/url.js +1 -1
- package/dist/production/lib/router/utils.d.ts +30 -5
- package/dist/production/lib/router/utils.js +8 -5
- package/dist/production/lib/rsc/browser.js +3 -2
- package/dist/production/lib/rsc/errorBoundaries.js +1 -1
- package/dist/production/lib/rsc/html-stream/browser.js +1 -1
- package/dist/production/lib/rsc/html-stream/server.js +25 -15
- package/dist/production/lib/rsc/route-modules.js +1 -1
- package/dist/production/lib/rsc/server.rsc.d.ts +10 -2
- package/dist/production/lib/rsc/server.ssr.js +25 -21
- package/dist/production/lib/server-runtime/build.d.ts +14 -0
- package/dist/production/lib/server-runtime/cookies.js +1 -1
- package/dist/production/lib/server-runtime/crypto.js +1 -1
- package/dist/production/lib/server-runtime/data.js +1 -1
- package/dist/production/lib/server-runtime/dev.js +1 -1
- package/dist/production/lib/server-runtime/entry.js +1 -1
- package/dist/production/lib/server-runtime/errors.js +1 -1
- package/dist/production/lib/server-runtime/headers.js +1 -1
- package/dist/production/lib/server-runtime/invariant.js +1 -1
- package/dist/production/lib/server-runtime/mode.js +1 -1
- package/dist/production/lib/server-runtime/routeMatching.js +3 -4
- package/dist/production/lib/server-runtime/routes.d.ts +1 -1
- package/dist/production/lib/server-runtime/routes.js +1 -1
- package/dist/production/lib/server-runtime/server.js +18 -9
- package/dist/production/lib/server-runtime/serverHandoff.d.ts +17 -0
- package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions.js +1 -1
- package/dist/production/lib/server-runtime/single-fetch.js +1 -1
- package/dist/production/lib/server-runtime/urls.js +1 -1
- package/dist/production/lib/server-runtime/warnings.js +1 -1
- package/dist/production/lib/types/internal.js +1 -1
- package/dist/production/lib/types/route-data.d.ts +2 -2
- package/dist/production/lib/types/route-module-annotations.d.ts +1 -1
- package/dist/production/route-pattern.d.ts +3 -0
- package/dist/production/route-pattern.js +12 -0
- package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
- package/docs/explanation/lazy-route-discovery.md +4 -0
- package/docs/explanation/sessions-and-cookies.md +108 -4
- package/docs/how-to/custom-fetch.md +218 -0
- package/docs/how-to/data-strategy.md +23 -1
- package/docs/how-to/fetchers.md +6 -1
- package/docs/how-to/middleware.md +4 -1
- package/docs/how-to/optimize-revalidation.md +178 -5
- package/docs/how-to/pre-rendering.md +23 -1
- package/docs/how-to/react-server-components.md +26 -3
- package/docs/how-to/resource-routes.md +3 -0
- package/docs/how-to/route-discovery.md +145 -0
- package/docs/how-to/spa.md +2 -0
- package/docs/how-to/view-transitions.md +80 -13
- package/docs/start/data/actions.md +2 -1
- package/docs/start/data/route-object.md +21 -6
- package/docs/start/framework/actions.md +4 -2
- package/docs/start/framework/navigating.md +5 -9
- package/docs/start/framework/rendering.md +37 -0
- package/docs/start/framework/route-module.md +32 -5
- package/docs/upgrading/future.md +68 -0
- package/package.json +19 -1
|
@@ -1,12 +1,185 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Revalidation Optimization
|
|
3
|
-
hidden: true
|
|
4
3
|
---
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
# Revalidation Optimization
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
[MODES: framework, data]
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
<br/>
|
|
10
|
+
<br/>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
After a mutation or some navigations, React Router re-runs loaders so
|
|
13
|
+
the UI stays in sync with the server. That default is the right
|
|
14
|
+
starting point. When a loader is expensive, or a mutation cannot
|
|
15
|
+
affect that route's data, you can skip the reload.
|
|
16
|
+
|
|
17
|
+
<docs-warning>
|
|
18
|
+
Skipping revalidation can leave the UI out of sync with the server.
|
|
19
|
+
Prefer targeting a specific action or navigation, and fall back to
|
|
20
|
+
`defaultShouldRevalidate` instead of always returning `false`.
|
|
21
|
+
</docs-warning>
|
|
22
|
+
|
|
23
|
+
## Default behavior
|
|
24
|
+
|
|
25
|
+
The default behavior differs between Framework and Data Modes:
|
|
26
|
+
|
|
27
|
+
- **Framework Mode with SSR**
|
|
28
|
+
- Defaults to opt-out behavior - active loaders are revalidated on navigations and successful submissions ([`Link`][link], [`Form`][form], [`fetcher.submit`][use-fetcher])
|
|
29
|
+
- Failed submissions returning a 4xx/5xx status do not trigger revalidations by default
|
|
30
|
+
- **Framework "SPA Mode" and Data Mode**
|
|
31
|
+
- Defaults to opt-out behavior on successful submissions - active loaders are revalidated on successful submissions ([`Form`][form], [`fetcher.submit`][use-fetcher])
|
|
32
|
+
- Failed submissions returning a 4xx/5xx status do not trigger revalidations by default
|
|
33
|
+
- Defaults to opt-in behavior for GET navigations ([`Link`][link]) - active loaders are only revalidated if their dynamic params changed, or if any search params changed
|
|
34
|
+
- A GET navigation to the _exact_ same URL is treated like a page refresh and all loaders are revalidated.
|
|
35
|
+
|
|
36
|
+
Matched matched routes are handled independently - A child that skips
|
|
37
|
+
revalidation does not skip any ancestor routes.
|
|
38
|
+
|
|
39
|
+
[`fetcher.load`][use-fetcher] only revalidates by default after action
|
|
40
|
+
submissions and explicit [`useRevalidator`][use-revalidator] calls, not
|
|
41
|
+
on search-param or param-driven navigations.
|
|
42
|
+
|
|
43
|
+
A plain `fetch()` to a [resource route][resource-routes] does not
|
|
44
|
+
go through the router, so it does not revalidate loaders.
|
|
45
|
+
|
|
46
|
+
## Skip a route with `shouldRevalidate`
|
|
47
|
+
|
|
48
|
+
Export `shouldRevalidate` from the [route module][route-module]
|
|
49
|
+
(Framework Mode) or set it on the [route object][data-mode]
|
|
50
|
+
(Data Mode). Returning `false` skips **that route's** loader.
|
|
51
|
+
|
|
52
|
+
```tsx filename=app/routes/dashboard.tsx
|
|
53
|
+
// Framework Mode
|
|
54
|
+
export function shouldRevalidate() {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```tsx src/main.tsx
|
|
60
|
+
// Data Mode
|
|
61
|
+
createBrowserRouter([
|
|
62
|
+
{
|
|
63
|
+
path: "/dashboard",
|
|
64
|
+
loader: dashboardLoader,
|
|
65
|
+
shouldRevalidate: () => false,
|
|
66
|
+
Component: Dashboard,
|
|
67
|
+
},
|
|
68
|
+
]);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Always returning `false` opts that route out of the default
|
|
72
|
+
behavior completely, including cases you usually still want
|
|
73
|
+
(param changes, explicit [`useRevalidator`][use-revalidator]).
|
|
74
|
+
Prefer the conditional form below.
|
|
75
|
+
|
|
76
|
+
## Opt out of specific requests
|
|
77
|
+
|
|
78
|
+
Inspect
|
|
79
|
+
[`ShouldRevalidateFunctionArgs`][should-revalidate-args]
|
|
80
|
+
and return `defaultShouldRevalidate` for everything else.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import type { ShouldRevalidateFunctionArgs } from "react-router";
|
|
84
|
+
|
|
85
|
+
export function shouldRevalidate({
|
|
86
|
+
formMethod,
|
|
87
|
+
formAction,
|
|
88
|
+
defaultShouldRevalidate,
|
|
89
|
+
}: ShouldRevalidateFunctionArgs) {
|
|
90
|
+
if (
|
|
91
|
+
formMethod === "POST" &&
|
|
92
|
+
formAction?.endsWith("/analytics")
|
|
93
|
+
) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return defaultShouldRevalidate;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Other useful fields:
|
|
102
|
+
|
|
103
|
+
- `formData`, `json`, `text` — the submission body
|
|
104
|
+
- `actionResult`, `actionStatus` — the action's return value
|
|
105
|
+
- `currentUrl`, `nextUrl`, `currentParams`, `nextParams` —
|
|
106
|
+
the navigation
|
|
107
|
+
|
|
108
|
+
You can ignore search-param-only updates while still
|
|
109
|
+
revalidating when the pathname changes:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
export function shouldRevalidate({
|
|
113
|
+
currentUrl,
|
|
114
|
+
nextUrl,
|
|
115
|
+
defaultShouldRevalidate,
|
|
116
|
+
}: ShouldRevalidateFunctionArgs) {
|
|
117
|
+
if (currentUrl.pathname === nextUrl.pathname) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return defaultShouldRevalidate;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Skip revalidation for one event
|
|
126
|
+
|
|
127
|
+
Pass `defaultShouldRevalidate={false}` at the call site so you
|
|
128
|
+
do not have to change every route file. This works on
|
|
129
|
+
[`<Form>`][form], [`<Link>`][link], `<fetcher.Form>`, and as an
|
|
130
|
+
option to [`useSubmit`][use-submit], `fetcher.submit`,
|
|
131
|
+
[`useNavigate`][use-navigate], and
|
|
132
|
+
[`useSearchParams`][use-search-params].
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { Form, Link } from "react-router";
|
|
136
|
+
|
|
137
|
+
<Link
|
|
138
|
+
to="/search?q=shoes"
|
|
139
|
+
defaultShouldRevalidate={false}
|
|
140
|
+
>
|
|
141
|
+
Search Shoes
|
|
142
|
+
</Link>
|
|
143
|
+
|
|
144
|
+
<Form
|
|
145
|
+
method="post"
|
|
146
|
+
action="/analytics"
|
|
147
|
+
defaultShouldRevalidate={false}
|
|
148
|
+
>
|
|
149
|
+
<button>Track Click</button>
|
|
150
|
+
</Form>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
fetcher.submit(
|
|
155
|
+
{ intent: "save-progress" },
|
|
156
|
+
{
|
|
157
|
+
method: "post",
|
|
158
|
+
action: "/save-progress",
|
|
159
|
+
defaultShouldRevalidate: false,
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
If a matched route does **not** export `shouldRevalidate`, this
|
|
165
|
+
value is used directly for that loader. If it **does** export
|
|
166
|
+
`shouldRevalidate`, the value is passed in as
|
|
167
|
+
`defaultShouldRevalidate` and the route still has the final say.
|
|
168
|
+
|
|
169
|
+
That is why a child `shouldRevalidate` that always returns
|
|
170
|
+
`false` cannot hide a root reload after `fetcher.submit`. Either
|
|
171
|
+
also opt `root` out for that case, or pass
|
|
172
|
+
`defaultShouldRevalidate: false` at the call site when `root`
|
|
173
|
+
has no `shouldRevalidate` of its own.
|
|
174
|
+
|
|
175
|
+
[data-mode]: ../start/data/route-object#shouldrevalidate
|
|
176
|
+
[form]: ../api/components/Form
|
|
177
|
+
[link]: ../api/components/Link
|
|
178
|
+
[resource-routes]: ./resource-routes
|
|
179
|
+
[route-module]: ../start/framework/route-module#shouldrevalidate
|
|
180
|
+
[should-revalidate-args]: https://api.reactrouter.com/v8/interfaces/react-router.ShouldRevalidateFunctionArgs.html
|
|
181
|
+
[use-fetcher]: ../api/hooks/useFetcher
|
|
182
|
+
[use-navigate]: ../api/hooks/useNavigate
|
|
183
|
+
[use-revalidator]: ../api/hooks/useRevalidator
|
|
184
|
+
[use-search-params]: ../api/hooks/useSearchParams
|
|
185
|
+
[use-submit]: ../api/hooks/useSubmit
|
|
@@ -84,10 +84,11 @@ export default {
|
|
|
84
84
|
|
|
85
85
|
## Pre-Rendering with/without a Runtime Server
|
|
86
86
|
|
|
87
|
-
Pre-Rendering can be used
|
|
87
|
+
Pre-Rendering can be used with these `ssr` config values:
|
|
88
88
|
|
|
89
89
|
- Alongside a runtime SSR server with `ssr:true` (the default value)
|
|
90
90
|
- Deployed to a static file server with `ssr:false`
|
|
91
|
+
- Alongside a runtime API server with `ssr:"unstable_api-only"` (experimental)
|
|
91
92
|
|
|
92
93
|
### Pre-rendering with `ssr:true`
|
|
93
94
|
|
|
@@ -223,3 +224,24 @@ When pre-rendering with `ssr:false`, React Router will error at build time if yo
|
|
|
223
224
|
- If you are using a `loader` on a pre-rendered route that has child routes, you will need to make sure the parent `loaderData` can be determined at run-time properly by either:
|
|
224
225
|
- Pre-rendering all child routes so that the parent `loader` can be called at build-time for each child route path and rendered into a `.data` file, or
|
|
225
226
|
- Use a `clientLoader` on the parent that can be called at run-time for non-pre-rendered child paths
|
|
227
|
+
|
|
228
|
+
### Pre-rendering with API-only Mode (unstable)
|
|
229
|
+
|
|
230
|
+
<docs-warning>API-only Mode is experimental and may change in minor or patch releases. It is not supported in RSC Framework Mode.</docs-warning>
|
|
231
|
+
|
|
232
|
+
Use `ssr: "unstable_api-only"` with `prerender` to generate documents at build time while keeping a runtime server for loaders, actions, and route discovery:
|
|
233
|
+
|
|
234
|
+
```ts filename=react-router.config.ts
|
|
235
|
+
import type { Config } from "@react-router/dev/config";
|
|
236
|
+
|
|
237
|
+
export default {
|
|
238
|
+
ssr: "unstable_api-only",
|
|
239
|
+
prerender: ["/"],
|
|
240
|
+
} satisfies Config;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Document generation follows the same behavior as `ssr:false`: selected paths produce HTML and `.data` files, and other paths use a SPA fallback. The fallback is `__spa-fallback.html` when `/` is prerendered, or `index.html` otherwise.
|
|
244
|
+
|
|
245
|
+
Prerendered pages hydrate using their build-time data. Pages using the fallback fetch server loader data during initialization. Server loaders and actions are available on all routes, including those outside the prerender list, and normal loader revalidation remains enabled.
|
|
246
|
+
|
|
247
|
+
See [API-only Mode](../start/framework/rendering#api-only-mode-unstable) for deployment routing and configuring a separate API origin.
|
|
@@ -498,16 +498,39 @@ Using Server Components in loaders can be helpful for incremental adoption of RS
|
|
|
498
498
|
|
|
499
499
|
[Server Functions][react-server-functions-doc] are a React feature that allow you to call async functions executed on the server. They're defined with the [`"use server"`][use-server-docs] directive.
|
|
500
500
|
|
|
501
|
+
<docs-warning>
|
|
502
|
+
|
|
503
|
+
Treat every Server Function as a public endpoint. The client controls both the
|
|
504
|
+
Server Function identifier and request URL, so do not rely on route middleware
|
|
505
|
+
for authentication or authorization. Server Functions must perform their own
|
|
506
|
+
access control and input validation; use a route `action` when access control
|
|
507
|
+
should be middleware-driven.
|
|
508
|
+
|
|
509
|
+
</docs-warning>
|
|
510
|
+
|
|
501
511
|
```tsx
|
|
502
512
|
"use server";
|
|
503
513
|
|
|
514
|
+
import { unstable_getRequest as getRequest } from "react-router";
|
|
515
|
+
import { requireUser } from "./auth.ts";
|
|
516
|
+
|
|
504
517
|
export async function updateFavorite(formData: FormData) {
|
|
505
|
-
let
|
|
518
|
+
let user = await requireUser(getRequest());
|
|
519
|
+
let movieId = Number(formData.get("id"));
|
|
506
520
|
let intent = formData.get("intent");
|
|
521
|
+
|
|
522
|
+
if (
|
|
523
|
+
!Number.isSafeInteger(movieId) ||
|
|
524
|
+
movieId <= 0 ||
|
|
525
|
+
(intent !== "add" && intent !== "remove")
|
|
526
|
+
) {
|
|
527
|
+
throw new Error("Invalid form submission");
|
|
528
|
+
}
|
|
529
|
+
|
|
507
530
|
if (intent === "add") {
|
|
508
|
-
await addFavorite(
|
|
531
|
+
await addFavorite(user.id, movieId);
|
|
509
532
|
} else {
|
|
510
|
-
await removeFavorite(
|
|
533
|
+
await removeFavorite(user.id, movieId);
|
|
511
534
|
}
|
|
512
535
|
}
|
|
513
536
|
```
|
|
@@ -66,6 +66,8 @@ export function action(_: Route.ActionArgs) {
|
|
|
66
66
|
}
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
Calling this `action` through [`<Form>`][form] or [`useFetcher`][fetcher] still revalidates matched UI loaders. A plain `fetch()` to the resource URL does not. See [Revalidation Optimization][optimize-revalidation] for more info.
|
|
70
|
+
|
|
69
71
|
## Return Types
|
|
70
72
|
|
|
71
73
|
Resource Routes are flexible when it comes to the return type - you can return [`Response`][Response] instances or [`data()`][data] objects. A good general rule of thumb when deciding which type to use is:
|
|
@@ -124,3 +126,4 @@ export function action() {
|
|
|
124
126
|
[form]: ../api/components/Form
|
|
125
127
|
[await]: ../api/components/Await
|
|
126
128
|
[error-boundary]: ../start/framework/route-module#errorboundary
|
|
129
|
+
[optimize-revalidation]: ./optimize-revalidation
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Application-Controlled Route Discovery
|
|
3
|
+
unstable: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Application-Controlled Route Discovery
|
|
7
|
+
|
|
8
|
+
[MODES: framework]
|
|
9
|
+
|
|
10
|
+
<docs-warning>This API is unstable and may change without a major release. It supports conventional Framework Mode; RSC modes are not supported.</docs-warning>
|
|
11
|
+
|
|
12
|
+
Enable the future flag in `react-router.config.ts`:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import type { Config } from "@react-router/dev/config";
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
future: { unstable_customRouteDiscovery: true },
|
|
19
|
+
} satisfies Config;
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Without this flag, React Router keeps its existing discovery implementation and the hook is unavailable. The flag supports conventional Framework Mode only.
|
|
23
|
+
|
|
24
|
+
Keep `routeDiscovery.mode: "lazy"` (the default for SSR) and use `unstable_useRouteDiscovery` to choose when to discover destinations or load the complete route manifest.
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
let { state, discoverRoutes, loadAllRoutes } =
|
|
28
|
+
unstable_useRouteDiscovery({
|
|
29
|
+
onManifestMismatch: async (event) => {
|
|
30
|
+
// Handle mismatch, potentially by loading the entire manifest
|
|
31
|
+
await loadAllRoutes();
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// state: "partial" | "loading" | "complete"
|
|
36
|
+
|
|
37
|
+
// Eagerly discover specific paths
|
|
38
|
+
let result = await discoverRoutes(paths);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Imperative Route Discovery
|
|
42
|
+
|
|
43
|
+
You can control route discovery through `discoverRoutes()` without having to render links/forms on the page - this allows you to eagerly discover routes that are not directly linked from the current UI. Note that discovery only loads route metadata - it does not navigate, run loaders/actions, or prefetch route modules/stylesheets. Paths are batched within the manifest request URL limit and previously discovered paths are skipped.
|
|
44
|
+
|
|
45
|
+
`discoverRoutes(paths, { signal })` accepts application-root paths beginning with `/`. React Router applies the configured `basename` and ignores query strings and hashes. Relative paths and external URLs are not supported.
|
|
46
|
+
|
|
47
|
+
The result is `{ type: "success" }`, `{ type: "version-mismatch" }`, or `{ type: "aborted" }`. Success means the discovery operation completed successfully; it does not guarantee that a request was sent or that each pathname matches a route. Cached paths need no request, and a concurrent full-manifest load can supersede an incremental request failure. Unrecovered request failures and unrelated handler errors reject. Imperative mismatches do not automatically trigger a reload.
|
|
48
|
+
|
|
49
|
+
For example, the cart's checkout link automatically discovers the shipping route. Use the hook to discover later checkout steps when the cart mounts, even though those steps aren't linked from the cart:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { useEffect } from "react";
|
|
53
|
+
import {
|
|
54
|
+
Link,
|
|
55
|
+
unstable_useRouteDiscovery,
|
|
56
|
+
} from "react-router";
|
|
57
|
+
|
|
58
|
+
function Cart() {
|
|
59
|
+
let { discoverRoutes } = unstable_useRouteDiscovery();
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
let controller = new AbortController();
|
|
63
|
+
|
|
64
|
+
discoverRoutes(
|
|
65
|
+
["/checkout/billing", "/checkout/review"],
|
|
66
|
+
{ signal: controller.signal },
|
|
67
|
+
).catch(console.error);
|
|
68
|
+
|
|
69
|
+
return () => controller.abort();
|
|
70
|
+
}, [discoverRoutes]);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<>
|
|
74
|
+
{/* Render cart items and totals here. */}
|
|
75
|
+
<Link to="/checkout/shipping">
|
|
76
|
+
Proceed to checkout
|
|
77
|
+
</Link>
|
|
78
|
+
</>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Completing discovery
|
|
84
|
+
|
|
85
|
+
`loadAllRoutes()` loads the running client's versioned manifest asset to complete its route tree, even if that client is stale. This merges all missing routes into the running router and thus prevents further incremental discovery. The exposed `version` remains the version of the running client. With `routeDiscovery.mode: "initial"`, SPA Mode, or development's full manifest, the state is already `complete` and the operations are successful no-ops.
|
|
86
|
+
|
|
87
|
+
The shared `state` moves from `partial` to `loading` to `complete`. Concurrent calls share a load, and calls after completion resolve immediately. Failures reject and return the state to `partial`; applications can catch the error and choose whether to reload.
|
|
88
|
+
|
|
89
|
+
Retain the client's versioned manifest **and its referenced assets** for as long as that client is supported. Server routing and loader/action contracts must also remain compatible. Loading build A's manifest does not make an incompatible build B backend safe for build A's client.
|
|
90
|
+
|
|
91
|
+
## Handling manifest mismatches
|
|
92
|
+
|
|
93
|
+
You can provide custom handling for manifest version mismatches to avoid hard reloads, giving you a chance to complete the route tree, or cancel the navigation and provide a prompt for the user to reload (see below). A mismatch indicates different versions, not which version is newer.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
unstable_useRouteDiscovery({
|
|
97
|
+
onManifestMismatch: async (event) => {
|
|
98
|
+
await event.loadAllRoutes();
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
After mismatch handlers settle, surviving operations continue only if the tree is complete. Otherwise navigations and fetchers are canceled and their pending state settles. Canceling a navigation also cancels any pending revalidation - both return to idle and the current page and data remain in place without another loader pass. Similarly, interrupted fetcher loads settle with their existing data. If an action already changed server data, the displayed data may remain stale until a later navigation or explicit revalidation.
|
|
104
|
+
|
|
105
|
+
Router-managed Back/Forward cancellations restore the prior history position. Additional browser Back/Forward presses during restoration can leave the URL and rendered route out of sync or navigate to another entry; preserving the current screen is not guaranteed in that case.
|
|
106
|
+
|
|
107
|
+
Full-manifest load failures remain catchable and do not render route error boundaries, whether the handler calls `event.loadAllRoutes()` or the function returned by the hook. Uncaught full-load failures are reported through `<HydratedRouter onError>` or `console.error`, and the incomplete operation is canceled. Unrelated handler errors follow normal discovery error handling.
|
|
108
|
+
|
|
109
|
+
## Temporary overrides
|
|
110
|
+
|
|
111
|
+
Custom mismatch handlers override built-in recovery. Calling the hook without a handler does not override anything. Removing the last custom mismatch handler restores automatic recovery.
|
|
112
|
+
|
|
113
|
+
Call `event.defaultBehavior()` to explicitly run built-in recovery: eager mismatches warn, navigation mismatches reload the destination, fetcher mismatches reload the current document, and imperative mismatches return without reloading. Repeated calls for one shared mismatch event reuse the same promise. It does not invoke other custom handlers or stop their propagation.
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
unstable_useRouteDiscovery({
|
|
117
|
+
async onManifestMismatch(event) {
|
|
118
|
+
if (hasUnsavedChanges) {
|
|
119
|
+
event.stopPropagation();
|
|
120
|
+
setShowReloadDialog(true);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
await event.defaultBehavior();
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Custom handlers run in reverse registration order, with each awaited before the next. `stopPropagation()` prevents earlier handlers from running; it does not complete discovery or allow an operation to continue against an incomplete tree.
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
unstable_useRouteDiscovery({
|
|
132
|
+
onManifestMismatch(event) {
|
|
133
|
+
if (hasUnsavedChanges) {
|
|
134
|
+
event.stopPropagation();
|
|
135
|
+
setShowReloadDialog(true);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
An editor mounted after a root recovery handler can use this to preserve a draft. Unmounting removes its registration and reveals earlier handlers. Callback updates preserve registration priority. This is registration order, not a guarantee that the deepest component runs first.
|
|
142
|
+
|
|
143
|
+
Eager discovery waits for the initialized route tree to commit so a root handler can intercept the first request. It does not wait for every suspended descendant or guarantee an idle main thread. Discovery needed before handlers can mount retains normal lazy behavior.
|
|
144
|
+
|
|
145
|
+
Without mismatch handlers, existing automatic recovery for eager/navigation/fetcher discovery remains unchanged. Deployment polling and staleness policy remain application-owned.
|
package/docs/how-to/spa.md
CHANGED
|
@@ -22,6 +22,8 @@ Typical Single Page apps send a mostly blank `index.html` template with little m
|
|
|
22
22
|
|
|
23
23
|
<docs-info>SPA Mode is a special form of "Pre-Rendering" that allows you to serve all paths in your application from the same HTML file. Please refer to the [Pre-Rendering](./pre-rendering) guide if you want to do more extensive pre-rendering.</docs-info>
|
|
24
24
|
|
|
25
|
+
<docs-info>If you want a SPA frontend backed by React Router server loaders and actions, see experimental [API-only Mode](../start/framework/rendering#api-only-mode-unstable).</docs-info>
|
|
26
|
+
|
|
25
27
|
## 1. Disable Runtime Server Rendering
|
|
26
28
|
|
|
27
29
|
Server rendering is enabled by default. Set the `ssr` flag to `false` in `react-router.config.ts` to disable it.
|
|
@@ -4,16 +4,78 @@ title: View Transitions
|
|
|
4
4
|
|
|
5
5
|
# View Transitions
|
|
6
6
|
|
|
7
|
-
[MODES: framework, data]
|
|
7
|
+
[MODES: framework, data, declarative]
|
|
8
8
|
|
|
9
9
|
<br/>
|
|
10
10
|
<br/>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Use React's [`<ViewTransition>`][react-view-transition] component to animate client-side navigations. This requires React and React DOM 19.3 or later.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
<docs-warning>
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
React Router's `viewTransition` props/options, `useViewTransitionState` hook, and `NavLink`'s `isTransitioning` render prop and `transitioning` class are deprecated in favor of React's `<ViewTransition>` component. The existing APIs continue to work, including with older React versions.
|
|
17
|
+
|
|
18
|
+
</docs-warning>
|
|
19
|
+
|
|
20
|
+
## Navigation View Transitions with React
|
|
21
|
+
|
|
22
|
+
Wrap the outlet in a persistent `<ViewTransition>` boundary in a shared layout route. In Framework Mode, this can be your root route's default export; in Data and Declarative modes, use it as the component for a parent route with children:
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { ViewTransition } from "react";
|
|
26
|
+
import { Link, Outlet } from "react-router";
|
|
27
|
+
|
|
28
|
+
export default function PageLayout() {
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<nav>
|
|
32
|
+
<Link to="/">Home</Link>
|
|
33
|
+
<Link to="/about">About</Link>
|
|
34
|
+
</nav>
|
|
35
|
+
<ViewTransition>
|
|
36
|
+
<main>
|
|
37
|
+
<Outlet />
|
|
38
|
+
</main>
|
|
39
|
+
</ViewTransition>
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Navigations that update the outlet cross-fade its contents. Keep the boundary mounted across navigations; you don't need to key it by location or remount the route tree. The navigation stays outside the animated region. Other updates inside the boundary can also animate when they run in a React Transition.
|
|
46
|
+
|
|
47
|
+
React Router wraps router state updates in [`React.startTransition`][start-transition] by default, which lets React activate `<ViewTransition>` without additional configuration. React Transitions must remain enabled for navigation view transitions to work: do not set `useTransitions={false}` on your router.
|
|
48
|
+
|
|
49
|
+
This applies to navigations from `<Link>`, `<NavLink>`, and `<Form>`, as well as programmatic navigation with `useNavigate` and `useSubmit`. No additional `startTransition` wrapper is needed.
|
|
50
|
+
|
|
51
|
+
### Migrating from React Router's view transitions
|
|
52
|
+
|
|
53
|
+
- Remove `viewTransition` from `<Link>`, `<NavLink>`, and `<Form>`, and remove `viewTransition: true` from navigation/submission options. React coordinates `document.startViewTransition()` itself; enabling both implementations can interrupt animations.
|
|
54
|
+
- Replace `useViewTransitionState`, `isTransitioning`, and `.transitioning` styles with React's [View Transition classes][react-view-transition-classes]. Those router APIs only track the legacy transitions.
|
|
55
|
+
- For shared elements such as an image moving from a list to a detail route, wrap each side in `<ViewTransition name="...">` with the same unique name. See React's [shared element example][react-shared-elements].
|
|
56
|
+
- Keep React Transitions enabled. `useTransitions={false}` and synchronous updates such as `flushSync` can prevent React's animations.
|
|
57
|
+
|
|
58
|
+
React's component is not a drop-in replacement for every legacy animation. In particular, React [skips view transitions for synchronous updates from `popstate`][react-router-transitions], so browser Back/Forward navigations may not animate. Browsers without View Transitions support still navigate normally.
|
|
59
|
+
|
|
60
|
+
Respect reduced-motion preferences when adding animations:
|
|
61
|
+
|
|
62
|
+
```css
|
|
63
|
+
@media (prefers-reduced-motion: reduce) {
|
|
64
|
+
::view-transition-group(*),
|
|
65
|
+
::view-transition-old(*),
|
|
66
|
+
::view-transition-new(*) {
|
|
67
|
+
animation: none !important;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Legacy View Transitions (Deprecated)
|
|
73
|
+
|
|
74
|
+
The following examples document the deprecated APIs for existing applications. These APIs are available in Framework and Data modes.
|
|
75
|
+
|
|
76
|
+
### Basic View Transition
|
|
77
|
+
|
|
78
|
+
#### 1. Enable view transitions on navigation
|
|
17
79
|
|
|
18
80
|
The simplest way to enable view transitions is by adding the `viewTransition` prop to your `Link`, `NavLink`, or `Form` components. This automatically wraps the navigation update in `document.startViewTransition()`.
|
|
19
81
|
|
|
@@ -25,7 +87,7 @@ The simplest way to enable view transitions is by adding the `viewTransition` pr
|
|
|
25
87
|
|
|
26
88
|
Without any additional CSS, this provides a basic cross-fade animation between pages.
|
|
27
89
|
|
|
28
|
-
|
|
90
|
+
#### 2. Enable view transitions with programmatic navigation
|
|
29
91
|
|
|
30
92
|
When using programmatic navigation with the `useNavigate` hook, you can enable view transitions by passing the `viewTransition: true` option:
|
|
31
93
|
|
|
@@ -51,11 +113,11 @@ This provides the same cross-fade animation as using the `viewTransition` prop o
|
|
|
51
113
|
|
|
52
114
|
For more information on using the View Transitions API, please refer to the ["Smooth transitions with the View Transition API" guide][view-transitions-guide] from the Google Chrome team.
|
|
53
115
|
|
|
54
|
-
|
|
116
|
+
### Image Gallery Example
|
|
55
117
|
|
|
56
118
|
Let's build an image gallery that demonstrates how to trigger and use view transitions. We'll create a list of images that expand into a detail view with smooth animations.
|
|
57
119
|
|
|
58
|
-
|
|
120
|
+
#### 1. Create the image gallery route
|
|
59
121
|
|
|
60
122
|
```tsx filename=routes/image-gallery.tsx
|
|
61
123
|
import { NavLink } from "react-router";
|
|
@@ -91,7 +153,7 @@ export default function ImageGalleryRoute() {
|
|
|
91
153
|
}
|
|
92
154
|
```
|
|
93
155
|
|
|
94
|
-
|
|
156
|
+
#### 2. Add transition styles
|
|
95
157
|
|
|
96
158
|
Define view transition names and animations for elements that should transition smoothly between routes.
|
|
97
159
|
|
|
@@ -127,7 +189,7 @@ Define view transition names and animations for elements that should transition
|
|
|
127
189
|
}
|
|
128
190
|
```
|
|
129
191
|
|
|
130
|
-
|
|
192
|
+
#### 3. Create the image detail route
|
|
131
193
|
|
|
132
194
|
The detail view needs to use the same view transition names to create a seamless animation.
|
|
133
195
|
|
|
@@ -151,7 +213,7 @@ export default function ImageDetailsRoute({
|
|
|
151
213
|
}
|
|
152
214
|
```
|
|
153
215
|
|
|
154
|
-
|
|
216
|
+
#### 4. Add matching transition styles for the detail view
|
|
155
217
|
|
|
156
218
|
```css filename=app.css
|
|
157
219
|
/* Match transition names from the list view */
|
|
@@ -169,11 +231,11 @@ export default function ImageDetailsRoute({
|
|
|
169
231
|
}
|
|
170
232
|
```
|
|
171
233
|
|
|
172
|
-
|
|
234
|
+
### Advanced Usage
|
|
173
235
|
|
|
174
236
|
You can control view transitions more precisely using either render props or the `useViewTransitionState` hook.
|
|
175
237
|
|
|
176
|
-
|
|
238
|
+
#### 1. Using render props
|
|
177
239
|
|
|
178
240
|
```tsx filename=routes/image-gallery.tsx
|
|
179
241
|
<NavLink to={`/image/${idx}`} viewTransition>
|
|
@@ -201,7 +263,7 @@ You can control view transitions more precisely using either render props or the
|
|
|
201
263
|
</NavLink>
|
|
202
264
|
```
|
|
203
265
|
|
|
204
|
-
|
|
266
|
+
#### 2. Using the `useViewTransitionState` hook
|
|
205
267
|
|
|
206
268
|
```tsx filename=routes/image-gallery.tsx
|
|
207
269
|
function NavImage(props: { src: string; idx: number }) {
|
|
@@ -234,4 +296,9 @@ function NavImage(props: { src: string; idx: number }) {
|
|
|
234
296
|
```
|
|
235
297
|
|
|
236
298
|
[view-transitions-api]: https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition
|
|
299
|
+
[react-view-transition]: https://react.dev/reference/react/ViewTransition
|
|
300
|
+
[start-transition]: https://react.dev/reference/react/startTransition
|
|
301
|
+
[react-view-transition-classes]: https://react.dev/reference/react/ViewTransition#view-transition-class
|
|
302
|
+
[react-shared-elements]: https://react.dev/reference/react/ViewTransition#animating-a-shared-element
|
|
303
|
+
[react-router-transitions]: https://react.dev/reference/react/ViewTransition#building-view-transition-enabled-routers
|
|
237
304
|
[view-transitions-guide]: https://developer.chrome.com/docs/web-platform/view-transitions
|
|
@@ -9,7 +9,7 @@ order: 5
|
|
|
9
9
|
|
|
10
10
|
## Defining Actions
|
|
11
11
|
|
|
12
|
-
Data mutations are done through Route actions defined on the `action` property of a route object. When the action completes, all loader data on the page is revalidated to keep your UI in sync with the data without writing any code to do it.
|
|
12
|
+
Data mutations are done through Route actions defined on the `action` property of a route object. When the action completes, all loader data on the page is revalidated to keep your UI in sync with the data without writing any code to do it. To skip some or all of those reloads, see [Revalidation Optimization][optimize-revalidation].
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { createBrowserRouter } from "react-router";
|
|
@@ -136,3 +136,4 @@ function Project() {
|
|
|
136
136
|
Next: [Navigating](./navigating)
|
|
137
137
|
|
|
138
138
|
[fetchers]: ../../how-to/fetchers
|
|
139
|
+
[optimize-revalidation]: ../../how-to/optimize-revalidation
|