Skip to content

Commit 8c7afc5

Browse files
committed
Merge branch 'v7' into markdalgleish/server-runtime-to-react-router
2 parents bab6898 + 97be8ab commit 8c7afc5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+751
-335
lines changed

docs/upgrading/v6.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Using `React.lazy` inside of a component is incompatible with `React.useTransiti
135135

136136
### v7_fetcherPersist
137137

138-
<docs-warning>If you are not using a `createBrowserRouter` you can skip this</docs-warning>
138+
<docs-warning>If you are not using a `<RouterProvider>` you can skip this</docs-warning>
139139

140140
**Background**
141141

@@ -157,7 +157,7 @@ It's unlikely to affect your app. You may want to check any usage of `useFetcher
157157

158158
### v7_normalizeFormMethod
159159

160-
<docs-warning>If you are not using a `createBrowserRouter` you can skip this</docs-warning>
160+
<docs-warning>If you are not using a `<RouterProvider>` you can skip this</docs-warning>
161161

162162
This normalizes `formMethod` fields as uppercase HTTP methods to align with the `fetch()` behavior. [View the CHANGELOG](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#futurev7_normalizeformmethod) for more information.
163163

@@ -186,7 +186,7 @@ If any of your code is checking for lowercase HTTP methods, you will need to upd
186186

187187
### v7_partialHydration
188188

189-
<docs-warning>If you are not using a `createBrowserRouter` you can skip this</docs-warning>
189+
<docs-warning>If you are not using a `<RouterProvider>` you can skip this</docs-warning>
190190

191191
This allows SSR frameworks to provide only partial hydration data. It's unlikely you need to worry about this, just turn the flag on. [View the CHANGELOG](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#partial-hydration) for more information.
192192

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
---
2+
title: Adopting Vite (Routes)
3+
---
4+
5+
# Adopting Vite (Routes)
6+
7+
If you are using `<RouterProvider>` please see [Adopting Route Modules from RouterProvider](./vite-router-provider) instead.
8+
9+
If you are using `<Routes>` this is the right place.
10+
11+
The React Router vite plugin adds framework features to React Router. This document wil help you adopt the plugin in your app if you'd like.
12+
13+
## Features
14+
15+
The Vite plugin adds:
16+
17+
- Route loaders, actions, and automatic data revalidation
18+
- Typesafe Routes Modules
19+
- Typesafe Route paths across your app
20+
- Automatic route code-splitting
21+
- Automatic scroll restoration across navigations
22+
- Optional Static pre-rendering
23+
- Optional Server rendering
24+
- Optional React Server Components
25+
26+
The initial setup will likely be a bit of a pain, but once complete, adopting the new features is incremental, you can do one route at a time.
27+
28+
## 1. Install Vite
29+
30+
First install the React Router vite plugin:
31+
32+
```shellscript nonumber
33+
npm install -D @react-router/dev
34+
```
35+
36+
Then swap out the React plugin for React Router. The `react` key accepts the same options as the React plugin.
37+
38+
```diff filename=vite.config.ts
39+
-import react from '@vitejs/plugin-react'
40+
+import { plugin as app } from "@react-router/vite";
41+
import { defineConfig } from "vite";
42+
43+
44+
export default defineConfig({
45+
plugins: [
46+
- react(reactOptions)
47+
+ app({ react: reactOptions })
48+
],
49+
});
50+
```
51+
52+
## 2. Add the Root entry point
53+
54+
In a typical Vite app, the `index.html` file is the entry point for bundling. The React Router Vite plugin moves the entry point to a `root.tsx` file so you can use React to render the shell of your app instead of static HTML, and eventually upgrade to Server Rendering if you want.
55+
56+
For example, if your current `index.html` looks like this:
57+
58+
```html filename="index.html"
59+
<!DOCTYPE html>
60+
<html lang="en">
61+
<head>
62+
<meta charset="UTF-8" />
63+
<meta
64+
name="viewport"
65+
content="width=device-width, initial-scale=1.0"
66+
/>
67+
<title>My App</title>
68+
</head>
69+
<body>
70+
<div id="root"></div>
71+
<script type="module" src="/src/main.tsx"></script>
72+
</body>
73+
</html>
74+
```
75+
76+
You would move that markup into `src/root.tsx` and delete `index.html`:
77+
78+
```tsx filename=src/root.tsx
79+
import {
80+
Scripts,
81+
Outlet,
82+
ScrollRestoration,
83+
} from "react-router";
84+
85+
export default function Root() {
86+
return (
87+
<html lang="en">
88+
<head>
89+
<meta charset="UTF-8" />
90+
<meta
91+
name="viewport"
92+
content="width=device-width, initial-scale=1.0"
93+
/>
94+
<title>My App</title>
95+
</head>
96+
<body>
97+
<Outlet />
98+
<ScrollRestoration />
99+
<Scripts />
100+
</body>
101+
</html>
102+
);
103+
}
104+
```
105+
106+
## 3. Add client entry module
107+
108+
In the typical Vite app setup the `index.html` file points to `src/main.tsx` as the client entry point. React Router uses a file named `src/entry.client.tsx` instead.
109+
110+
If your current `src/main.tsx` looks like this:
111+
112+
```tsx filename=src/main.tsx
113+
import "./index.css";
114+
import React from "react";
115+
import ReactDOM from "react-dom/client";
116+
import App from "./App.tsx";
117+
118+
ReactDOM.createRoot(
119+
document.getElementById("root")!
120+
).render(
121+
<React.StrictMode>
122+
<App />
123+
</React.StrictMode>
124+
);
125+
```
126+
127+
You would rename it to `entry.client.tsx` and have it look like this:
128+
129+
```tsx filename=src/entry.client.tsx
130+
import "./index.css";
131+
import React from "react";
132+
import ReactDOM from "react-dom/client";
133+
import { HydratedRouter } from "react-router";
134+
135+
ReactDOM.hydrateRoot(
136+
document,
137+
<StrictMode>
138+
<HydratedRouter />
139+
</StrictMode>
140+
);
141+
```
142+
143+
- Use `hydrateRoot` instead of `createRoot`
144+
- Render a `<HydratedRouter>` instead of your `<App/>` component
145+
- Note that we stopped rendering the `<App/>` component, it'll come back in a later step, for now we want to simply get the app booting with the new entry points.
146+
147+
## 4. Shuffle stuff around
148+
149+
Between `root.tsx` and `entry.client.tsx`, you may want to shuffle some stuff around between them.
150+
151+
In general:
152+
153+
- `root.tsx` contains any rendering things like context providers, layouts, styles, etc.
154+
- `entry.client.tsx` should be as minimal as possible
155+
- Remember to _not_ try to render your existing `<App/>` component so isolate steps
156+
157+
Note that your `root.tsx` file will be statically generated and served as the entry point of your app, so just that module will need to be compatible with server rendering. This is where most of your trouble will come.
158+
159+
## 5. Boot the app
160+
161+
At this point you should be able to to boot the app and see the root layout.
162+
163+
```shellscript
164+
npm react-router vite:dev
165+
```
166+
167+
- Search the [Upgrading Discussion](#TODO) category
168+
- Reach out for help on [Twitter](https://x.com/remix_run) or [Discord](https://rmx.as/discord)
169+
170+
Make sure you can boot your app at this point before moving on.
171+
172+
## 6. Configure Catchall Route
173+
174+
To get back to rendering your app, we'll configure a "catchall" route that matches all URLs so that your existing `<Routes>` get a chance to render.
175+
176+
Create a file at `src/routes.ts` and add this:
177+
178+
```ts filename=src/routes.ts
179+
import { defineRoutes } from "react-router/config";
180+
181+
export default defineRoutes([
182+
{
183+
path: "*",
184+
file: "src/catchall.tsx",
185+
},
186+
]);
187+
```
188+
189+
And then create the catchall route module and render your existing root App component within it.
190+
191+
```tsx filename=src/catchall.tsx
192+
import { defineRoute } from "react-router";
193+
import App from "./App";
194+
195+
export default defineRoute({
196+
Component() {
197+
return <App />;
198+
},
199+
});
200+
```
201+
202+
Your app should be back on the screen and working as usual!
203+
204+
## 6. Migrate a route to a Route Module
205+
206+
You can now incrementally migrate your routes to route modules.
207+
208+
Given an existing route like this:
209+
210+
```tsx filename=src/App.tsx
211+
// ...
212+
import Page from "./containers/page";
213+
214+
export default function App() {
215+
return (
216+
<Routes>
217+
<Route path="/pages/:id" element={<Page />} />
218+
</Routes>
219+
);
220+
}
221+
```
222+
223+
You can move the definition to a `routes.ts` file:
224+
225+
```tsx filename=src/routes.ts
226+
import { defineRoutes } from "react-router/config";
227+
228+
export default defineRoutes([
229+
{
230+
path: "/pages/:id",
231+
file: "./containers/page.tsx",
232+
},
233+
{
234+
path: "*",
235+
file: "src/catchall.tsx",
236+
},
237+
]);
238+
```
239+
240+
And then edit the route module to use `defineRoute`:
241+
242+
```tsx filename=src/pages/about.tsx
243+
import { defineRoute } from "react-router";
244+
245+
export default defineRoute({
246+
params: ["id"],
247+
248+
async clientLoader({ params }) {
249+
let page = await getPage(params.id);
250+
return page;
251+
},
252+
253+
Component({ data }) {
254+
return <h1>{data.title}</h1>;
255+
},
256+
});
257+
```
258+
259+
You'll now get inferred type safety with params, loader data, and more.
260+
261+
The first few routes you migrate are the hardest because you often have to access various abstractions a bit differently than before (like in a loader instead of from a hook or context). But once the trickiest bits get dealt with, you get into an incremental groove.
262+
263+
## Enable SSR and Pre-rendering
264+
265+
If you want to enable server rendering and static pre-rendering, you can do so with the `ssr` and `prerender` options in the bundler plugin.
266+
267+
```ts filename=vite.config.ts
268+
import { plugin as app } from "@react-router/vite";
269+
import { defineConfig } from "vite";
270+
271+
export default defineConfig({
272+
plugins: [
273+
app({
274+
ssr: true,
275+
async prerender() {
276+
return ["/", "/about", "/contact"];
277+
},
278+
}),
279+
],
280+
});
281+
```
282+
283+
See [Deploying][deploying] for more information on deploying a server.
284+
285+
[deploying]: ../start/deploying

0 commit comments

Comments
 (0)