You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Added a new `future.v7_partialHydration` future flag that enables partial hydration of a data router when Server-Side Rendering. This allows you to provide `hydrationData.loaderData` that has values for _some_ initially matched route loaders, but not all. When this flag is enabled, the router will call `loader` functions for routes that do not have hydration loader data during `router.initialize()`, and it will render down to the deepest provided `HydrateFallback` (up to the first route without hydration data) while it executes the unhydrated routes.
6
+
7
+
For example, the following router has a `root` and `index` route, but only provided `hydrationData.loaderData` for the `root` route. Because the `index` route has a `loader`, we need to run that during initialization. With `future.v7_partialHydration` specified, `<RouterProvider>` will render the `RootComponent` (because it has data) and then the `IndexFallback` (since it does not have data). Once `indexLoader` finishes, application will update and display `IndexComponent`.
8
+
9
+
```jsx
10
+
let router =createBrowserRouter(
11
+
[
12
+
{
13
+
id:"root",
14
+
path:"/",
15
+
loader: rootLoader,
16
+
Component: RootComponent,
17
+
Fallback: RootFallback,
18
+
children: [
19
+
{
20
+
id:"index",
21
+
index:true,
22
+
loader: indexLoader,
23
+
Component: IndexComponent,
24
+
HydrateFallback: IndexFallback,
25
+
},
26
+
],
27
+
},
28
+
],
29
+
{
30
+
future: {
31
+
v7_partialHydration:true,
32
+
},
33
+
hydrationData: {
34
+
loaderData: {
35
+
root: { message:"Hydrated from Root!" },
36
+
},
37
+
},
38
+
}
39
+
);
40
+
```
41
+
42
+
If the above example did not have an `IndexFallback`, then `RouterProvider` would instead render the `RootFallback` while it executed the `indexLoader`.
43
+
44
+
**Note:** When `future.v7_partialHydration` is provided, the `<RouterProvider fallbackElement>` prop is ignored since you can move it to a `Fallback` on your top-most route. The `fallbackElement` prop will be removed in React Router v7 when `v7_partialHydration` behavior becomes the standard behavior.
Copy file name to clipboardExpand all lines: docs/guides/ssr.md
+11Lines changed: 11 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -165,6 +165,14 @@ And with that you've got a server-side-rendered and hydrated application! For a
165
165
166
166
As mentioned above, server-side rendering is tricky at scale and for production-grade applications, and we strongly recommend checking out [Remix][remix] if that's your goal. But if you are going the manual route, here's a few additional concepts you may need to consider:
167
167
168
+
#### Hydration
169
+
170
+
A core concept of Server Side Rendering is [hydration][hydration] which involves "attaching" a client-side React application to server-rendered HTML. To do this correctly, we need to create our client-side React Router application in the same state that it was in during the server render. When your server render loaded data via `loader` functions, we need to send this data up so that we can create our client router with the same loader data for the initial render/hydration.
171
+
172
+
The basic usages of `<StaticRouterProvider>` and `createBrowserRouter` shown in this guide handle this for you internally, but if you need to take control over the hydration process you can disable the automatic hydration process via [`<StaticRouterProvider hydrate={false} />`][hydrate-false].
173
+
174
+
In some advanced use cases, you may want to partially hydrate a client-side React Router application. You can do this via the [`future.v7_partialHydration`][partialhydration] flag passed to `createBrowserRouter`.
175
+
168
176
#### Redirects
169
177
170
178
If any loaders redirect, `handler.query` will return the `Response` directly so you should check that and send a redirect response instead of attempting to render an HTML document:
@@ -309,3 +317,6 @@ Again, we recommend you give [Remix](https://remix.run) a look. It's the best wa
If you are using [Server-Side Rendering][ssr] and you are leveraging [partial hydration][partialhydration], then you can specify an Element/Component to render for non-hydrated routes during the initial hydration of the application.
9
+
10
+
<docs-info>If you do not wish to specify a React element (i.e., `hydrateFallbackElement={<MyFallback />}`) you may specify an `HydrateFallback` component instead (i.e., `HydrateFallback={MyFallback}`) and React Router will call `createElement` for you internally.</docs-info>
11
+
12
+
<docs-warning>This feature only works if using a data router, see [Picking a Router][pickingarouter]</docs-warning>
13
+
14
+
```tsx
15
+
let router =createBrowserRouter(
16
+
[
17
+
{
18
+
id: "root",
19
+
path: "/",
20
+
loader: rootLoader,
21
+
Component: Root,
22
+
children: [
23
+
{
24
+
id: "invoice",
25
+
path: "invoices/:id",
26
+
loader: loadInvoice,
27
+
Component: Invoice,
28
+
HydrateFallback: InvoiceSkeleton,
29
+
},
30
+
],
31
+
},
32
+
],
33
+
{
34
+
future: {
35
+
v7_partialHydration: true,
36
+
},
37
+
hydrationData: {
38
+
root: {
39
+
/*...*/
40
+
},
41
+
// No hydration data provided for the `invoice` route
42
+
},
43
+
}
44
+
);
45
+
```
46
+
47
+
<docs-warning>There is no default fallback and it will just render `null` at that route level, so it is recommended that you always provide your own fallback element.</docs-warning>
Copy file name to clipboardExpand all lines: docs/route/loader.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -85,6 +85,10 @@ function loader({ request }) {
85
85
86
86
Note that the APIs here are not React Router specific, but rather standard web objects: [Request][request], [URL][url], [URLSearchParams][urlsearchparams].
87
87
88
+
## `loader.hydrate`
89
+
90
+
If you are [Server-Side Rendering][ssr] and leveraging the `fututre.v7_partialHydration` flag for [Partial Hydration][partialhydration], then you may wish to opt-into running a route `loader` on initial hydration _even though it has hydration data_ (for example, to let a user prime a cache with the hydration data). To force a `loader` to run on hydration in a partial hydration scenario, you can set a `hydrate` property on the `loader` function:
91
+
88
92
## Returning Responses
89
93
90
94
While you can return anything you want from a loader and get access to it from [`useLoaderData`][useloaderdata], you can also return a web [Response][response].
@@ -174,3 +178,5 @@ For more details, read the [`errorElement`][errorelement] documentation.
Copy file name to clipboardExpand all lines: docs/route/route.md
+17-1Lines changed: 17 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -76,8 +76,10 @@ interface RouteObject {
76
76
loader?:LoaderFunction;
77
77
action?:ActionFunction;
78
78
element?:React.ReactNode|null;
79
-
Component?:React.ComponentType|null;
79
+
hydrateFallbackElement?:React.ReactNode|null;
80
80
errorElement?:React.ReactNode|null;
81
+
Component?:React.ComponentType|null;
82
+
HydrateFallback?:React.ComponentType|null;
81
83
ErrorBoundary?:React.ComponentType|null;
82
84
handle?:RouteObject["handle"];
83
85
shouldRevalidate?:ShouldRevalidateFunction;
@@ -354,6 +356,16 @@ Otherwise use `ErrorBoundary` and React Router will create the React Element for
354
356
355
357
Please see the [errorElement][errorelement] documentation for more details.
356
358
359
+
## `hydrateFallbackElement`/`HydrateFallback`
360
+
361
+
If you are using [Server-Side Rendering][ssr] and you are leveraging [partial hydration][partialhydration], then you can specify an Element/Component to render for non-hydrated routes during the initial hydration of the application.
362
+
363
+
<docs-warning>If you are not using a data router like [`createBrowserRouter`][createbrowserrouter], this will do nothing</docs-warning>
364
+
365
+
<docs-warning>This is only intended for more advanced uses cases such as Remix's [`clientLoader`][clientloader] functionality. Most SSR apps will not need to leverage these route properties.</docs-warning>
366
+
367
+
Please see the [hydrateFallbackElement][hydratefallbackelement] documentation for more details.
368
+
357
369
## `handle`
358
370
359
371
Any application-specific data. Please see the [useMatches][usematches] documentation for details and examples.
@@ -404,10 +416,14 @@ Please see the [lazy][lazy] documentation for more details.
|`v7_fetcherPersist`| Delay active fetcher cleanup until they return to an `idle` state |
122
122
|`v7_normalizeFormMethod`| Normalize `useNavigation().formMethod` to be an uppercase HTTP Method |
123
+
|`v7_partialHydration`| Support partial hydration for Server-rendered apps |
123
124
|`v7_prependBasename`| Prepend the router basename to navigate/fetch paths |
124
125
126
+
## `hydrationData`
127
+
128
+
When [Server-Rendering][ssr] and [opting-out of automatic hydration][hydrate-false], the `hydrationData` option allows you to pass in hydration data from your server-render. This will almost always be a subset of data from the `StaticHandlerContext` value you get back from [handler.query][query]:
129
+
130
+
```js
131
+
constrouter=createBrowserRouter(routes, {
132
+
hydrationData: {
133
+
loaderData: {
134
+
// [routeId]: serverLoaderData
135
+
},
136
+
// may also include `errors` and/or `actionData`
137
+
},
138
+
});
139
+
```
140
+
141
+
### Partial Hydration Data
142
+
143
+
You will almost always include a complete set of `loaderData` to hydrate a server-rendered app. But in advanced use-cases (such as Remix's [`clientLoader`][clientloader]), you may want to include `loaderData` for only _some_ routes that were rendered on the server. If you want to enable partial `loaderData` and opt-into granular [`route.HydrateFallback`][hydratefallback] usage, you will need to enable the `future.v7_partialHydration` flag. Prior to this flag, any provided `loaderData` was assumed to be complete and would not result in the execution of route loaders on initial hydration.
144
+
145
+
When this flag is specified, loaders will run on initial hydration in 2 scenarios:
146
+
147
+
- No hydration data is provided
148
+
- In these cases the `HydrateFallback` component will render on initial hydration
149
+
- The `loader.hydrate` property is set to `true`
150
+
- This allows you to run the `loader` even if you did not render a fallback on initial hydration (i.e., to prime a cache with hydration data)
151
+
152
+
```js
153
+
constrouter=createBrowserRouter(
154
+
[
155
+
{
156
+
id:"root",
157
+
loader: rootLoader,
158
+
Component: Root,
159
+
children: [
160
+
{
161
+
id:"index",
162
+
loader: indexLoader,
163
+
HydrateFallback: IndexSkeleton,
164
+
Component:Index,
165
+
},
166
+
],
167
+
},
168
+
],
169
+
{
170
+
future: {
171
+
v7_partialHydration:true,
172
+
},
173
+
hydrationData: {
174
+
loaderData: {
175
+
root:"ROOT DATA",
176
+
// No index data provided
177
+
},
178
+
},
179
+
}
180
+
);
181
+
```
182
+
125
183
## `window`
126
184
127
185
Useful for environments like browser devtool plugins or testing to use a different window than the global `window`.
@@ -134,3 +192,8 @@ Useful for environments like browser devtool plugins or testing to use a differe
Copy file name to clipboardExpand all lines: docs/routers/create-static-router.md
+27-1Lines changed: 27 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -55,10 +55,34 @@ export async function renderHtml(req) {
55
55
```ts
56
56
declarefunction createStaticRouter(
57
57
routes:RouteObject[],
58
-
context:StaticHandlerContext
58
+
context:StaticHandlerContext,
59
+
opts: {
60
+
future?: {
61
+
v7_partialHydration?:boolean;
62
+
};
63
+
}
59
64
):Router;
60
65
```
61
66
67
+
## `opts.future`
68
+
69
+
An optional set of [Future Flags][api-development-strategy] to enable for this Static Router. We recommend opting into newly released future flags sooner rather than later to ease your eventual migration to v7.
70
+
71
+
```js
72
+
constrouter=createBrowserRouter(routes, {
73
+
future: {
74
+
// Opt-into partial hydration
75
+
v7_partialHydration:true,
76
+
},
77
+
});
78
+
```
79
+
80
+
The following future flags are currently available:
0 commit comments