Push V1 app
This commit is contained in:
+403
@@ -0,0 +1,403 @@
|
||||
---
|
||||
title: Upgrading from Remix
|
||||
order: 3
|
||||
---
|
||||
|
||||
# Upgrading from Remix
|
||||
|
||||
<docs-info>
|
||||
|
||||
React Router v7 requires the following minimum versions:
|
||||
|
||||
- `node@20`
|
||||
- `react@18`
|
||||
- `react-dom@18`
|
||||
|
||||
</docs-info>
|
||||
|
||||
React Router v7 is the next major version of Remix after v2 (see our ["Incremental Path to React 19" blog post][incremental-path-to-react-19] for more information).
|
||||
|
||||
If you have enabled all [Remix v2 future flags][v2-future-flags], upgrading from Remix v2 to React Router v7 mainly involves updating dependencies.
|
||||
|
||||
<docs-info>
|
||||
|
||||
The majority of steps 2-8 can be automatically updated using a [codemod][codemod] created by community member [James Restall][jrestall].
|
||||
|
||||
</docs-info>
|
||||
|
||||
## 1. Adopt future flags
|
||||
|
||||
**👉 Adopt future flags**
|
||||
|
||||
Adopt all existing [future flags][v2-future-flags] in your Remix v2 application.
|
||||
|
||||
## 2. Update dependencies
|
||||
|
||||
Most of the "shared" APIs that used to be re-exported through the runtime-specific packages (`@remix-run/node`, `@remix-run/cloudflare`, etc.) have all been collapsed into `react-router` in v7. So instead of importing from `@react-router/node` or `@react-router/cloudflare`, you'll import those directly from `react-router`.
|
||||
|
||||
```diff
|
||||
-import { redirect } from "@remix-run/node";
|
||||
+import { redirect } from "react-router";
|
||||
```
|
||||
|
||||
The only APIs you should be importing from the runtime-specific packages in v7 are APIs that are specific to that runtime, such as `createFileSessionStorage` for Node and `createWorkersKVSessionStorage` for Cloudflare.
|
||||
|
||||
**👉 Run the codemod (automated)**
|
||||
|
||||
You can automatically update your packages and imports with the following [codemod][codemod]. This codemod updates all of your packages and imports. Be sure to commit any pending changes before running the codemod, in case you need to revert.
|
||||
|
||||
```shellscript nonumber
|
||||
npx codemod remix/2/react-router/upgrade
|
||||
```
|
||||
|
||||
**👉 Install the new dependencies**
|
||||
|
||||
After the codemod updates your dependencies, you need to install the dependencies to remove Remix packages and add the new React Router packages.
|
||||
|
||||
```shellscript nonumber
|
||||
npm install
|
||||
```
|
||||
|
||||
**👉 Update your dependencies (manual)**
|
||||
|
||||
If you prefer not to use the codemod, you can manually update your dependencies.
|
||||
|
||||
<details>
|
||||
<summary>Expand to see a table of package name changes in alphabetical order</summary>
|
||||
|
||||
| Remix v2 Package | | React Router v7 Package |
|
||||
| ---------------------------------- | --- | ------------------------------------------- |
|
||||
| `@remix-run/architect` | ➡️ | `@react-router/architect` |
|
||||
| `@remix-run/cloudflare` | ➡️ | `@react-router/cloudflare` |
|
||||
| `@remix-run/dev` | ➡️ | `@react-router/dev` |
|
||||
| `@remix-run/express` | ➡️ | `@react-router/express` |
|
||||
| `@remix-run/fs-routes` | ➡️ | `@react-router/fs-routes` |
|
||||
| `@remix-run/node` | ➡️ | `@react-router/node` |
|
||||
| `@remix-run/react` | ➡️ | `react-router` |
|
||||
| `@remix-run/route-config` | ➡️ | `@react-router/dev` |
|
||||
| `@remix-run/routes-option-adapter` | ➡️ | `@react-router/remix-routes-option-adapter` |
|
||||
| `@remix-run/serve` | ➡️ | `@react-router/serve` |
|
||||
| `@remix-run/server-runtime` | ➡️ | `react-router` |
|
||||
| `@remix-run/testing` | ➡️ | `react-router` |
|
||||
|
||||
</details>
|
||||
|
||||
## 3. Change `scripts` in `package.json`
|
||||
|
||||
<docs-info>
|
||||
|
||||
If you used the codemod you can skip this step as it was automatically completed.
|
||||
|
||||
</docs-info>
|
||||
|
||||
**👉 Update the scripts in your `package.json`**
|
||||
|
||||
| Script | Remix v2 | | React Router v7 |
|
||||
| ----------- | ----------------------------------- | --- | ------------------------------------------ |
|
||||
| `dev` | `remix vite:dev` | ➡️ | `react-router dev` |
|
||||
| `build` | `remix vite:build` | ➡️ | `react-router build` |
|
||||
| `start` | `remix-serve build/server/index.js` | ➡️ | `react-router-serve build/server/index.js` |
|
||||
| `typecheck` | `tsc` | ➡️ | `react-router typegen && tsc` |
|
||||
|
||||
## 4. Add a `routes.ts` file
|
||||
|
||||
<docs-info>
|
||||
|
||||
If you used the codemod _and_ Remix v2 `v3_routeConfig` flag, you can skip this step as it was automatically completed.
|
||||
|
||||
</docs-info>
|
||||
|
||||
In React Router v7 you define your routes using the `app/routes.ts` file. View the [routing documentation][routing] for more information.
|
||||
|
||||
**👉 Update dependencies (if using Remix v2 `v3_routeConfig` flag)**
|
||||
|
||||
```diff filename=app/routes.ts
|
||||
-import { type RouteConfig } from "@remix-run/route-config";
|
||||
-import { flatRoutes } from "@remix-run/fs-routes";
|
||||
-import { remixRoutesOptionAdapter } from "@remix-run/routes-option-adapter";
|
||||
+import { type RouteConfig } from "@react-router/dev/routes";
|
||||
+import { flatRoutes } from "@react-router/fs-routes";
|
||||
+import { remixRoutesOptionAdapter } from "@react-router/remix-routes-option-adapter";
|
||||
|
||||
export default [
|
||||
// however your routes are defined
|
||||
] satisfies RouteConfig;
|
||||
```
|
||||
|
||||
**👉 Add a `routes.ts` file (if _not_ using Remix v2 `v3_routeConfig` flag)**
|
||||
|
||||
```shellscript nonumber
|
||||
touch app/routes.ts
|
||||
```
|
||||
|
||||
For backwards-compatibility, there are a few ways to adopt `routes.ts` to align with your route setup in Remix v2:
|
||||
|
||||
1. If you were using the "flat routes" [file-based convention][fs-routing], you can continue to use that via the new `@react-router/fs-routes` package:
|
||||
|
||||
```ts filename=app/routes.ts
|
||||
import { type RouteConfig } from "@react-router/dev/routes";
|
||||
import { flatRoutes } from "@react-router/fs-routes";
|
||||
|
||||
export default flatRoutes() satisfies RouteConfig;
|
||||
```
|
||||
|
||||
2. If you were using the "nested" convention from Remix v1 via the `@remix-run/v1-route-convention` package, you can continue using that as well in conjunction with `@react-router/remix-routes-option-adapter`:
|
||||
|
||||
```ts filename=app/routes.ts
|
||||
import { type RouteConfig } from "@react-router/dev/routes";
|
||||
import { remixRoutesOptionAdapter } from "@react-router/remix-routes-option-adapter";
|
||||
import { createRoutesFromFolders } from "@remix-run/v1-route-convention";
|
||||
|
||||
export default remixRoutesOptionAdapter(
|
||||
createRoutesFromFolders,
|
||||
) satisfies RouteConfig;
|
||||
```
|
||||
|
||||
3. If you were using the `routes` option to define config-based routes, you can keep that config via `@react-router/remix-routes-option-adapter`:
|
||||
|
||||
```ts filename=app/routes.ts
|
||||
import { type RouteConfig } from "@react-router/dev/routes";
|
||||
import { remixRoutesOptionAdapter } from "@react-router/remix-routes-option-adapter";
|
||||
|
||||
export default remixRoutesOptionAdapter(
|
||||
(defineRoutes) => {
|
||||
return defineRoutes((route) => {
|
||||
route("/", "home/route.tsx", { index: true });
|
||||
route("about", "about/route.tsx");
|
||||
route("", "concerts/layout.tsx", () => {
|
||||
route("trending", "concerts/trending.tsx");
|
||||
route(":city", "concerts/city.tsx");
|
||||
});
|
||||
});
|
||||
},
|
||||
) satisfies RouteConfig;
|
||||
```
|
||||
|
||||
- Be sure to also remove the `routes` option in your `vite.config.ts`:
|
||||
|
||||
```diff filename=vite.config.ts
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
remix({
|
||||
ssr: true,
|
||||
- ignoredRouteFiles: ['**/*'],
|
||||
- routes(defineRoutes) {
|
||||
- return defineRoutes((route) => {
|
||||
- route("/somewhere/cool/*", "catchall.tsx");
|
||||
- });
|
||||
- },
|
||||
})
|
||||
tsconfigPaths(),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## 5. Add a React Router config
|
||||
|
||||
**👉 Add `react-router.config.ts` your project**
|
||||
|
||||
The config that was previously passed to the `remix` plugin in `vite.config.ts` is now exported from `react-router.config.ts`.
|
||||
|
||||
Note: At this point you should remove the v3 future flags you added in step 1.
|
||||
|
||||
```shellscript nonumber
|
||||
touch react-router.config.ts
|
||||
```
|
||||
|
||||
```diff filename=vite.config.ts
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
- remix({
|
||||
- ssr: true,
|
||||
- future: {/* all the v3 flags */}
|
||||
- }),
|
||||
+ reactRouter(),
|
||||
tsconfigPaths(),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
```diff filename=react-router.config.ts
|
||||
+import type { Config } from "@react-router/dev/config";
|
||||
+export default {
|
||||
+ ssr: true,
|
||||
+} satisfies Config;
|
||||
```
|
||||
|
||||
## 6. Add React Router plugin to `vite.config`
|
||||
|
||||
<docs-info>
|
||||
|
||||
If you used the codemod you can skip this step as it was automatically completed.
|
||||
|
||||
</docs-info>
|
||||
|
||||
**👉 Add `reactRouter` plugin to `vite.config`**
|
||||
|
||||
Change `vite.config.ts` to import and use the new `reactRouter` plugin from `@react-router/dev/vite`:
|
||||
|
||||
```diff filename=vite.config.ts
|
||||
-import { vitePlugin as remix } from "@remix-run/dev";
|
||||
+import { reactRouter } from "@react-router/dev/vite";
|
||||
import { defineConfig } from "vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
- remix(),
|
||||
+ reactRouter(),
|
||||
tsconfigPaths(),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## 7. Enable type safety
|
||||
|
||||
<docs-info>
|
||||
|
||||
If you are not using TypeScript, you can skip this step.
|
||||
|
||||
</docs-info>
|
||||
|
||||
React Router automatically generates types for your route modules into a `.react-router/` directory at the root of your app. This directory is fully managed by React Router and should be gitignore'd. Learn more about the [new type safety features][type-safety].
|
||||
|
||||
**👉 Add `.react-router/` to `.gitignore`**
|
||||
|
||||
```txt
|
||||
.react-router/
|
||||
```
|
||||
|
||||
**👉 Update `tsconfig.json`**
|
||||
|
||||
Update the `types` field in your `tsconfig.json` to include:
|
||||
|
||||
- `.react-router/types/**/*` path in the `include` field
|
||||
- The appropriate `@react-router/*` package in the `types` field
|
||||
- `rootDirs` for simplified relative imports
|
||||
|
||||
```diff filename=tsconfig.json
|
||||
{
|
||||
"include": [
|
||||
/* ... */
|
||||
+ ".react-router/types/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
- "types": ["@remix-run/node", "vite/client"],
|
||||
+ "types": ["@react-router/node", "vite/client"],
|
||||
/* ... */
|
||||
+ "rootDirs": [".", "./.react-router/types"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. Rename components in entry files
|
||||
|
||||
<docs-info>
|
||||
|
||||
If you used the codemod you can skip this step as it was automatically completed.
|
||||
|
||||
</docs-info>
|
||||
|
||||
If you have an `entry.server.tsx` and/or an `entry.client.tsx` file in your application, you will need to update the main components in these files:
|
||||
|
||||
```diff filename=app/entry.server.tsx
|
||||
-import { RemixServer } from "@remix-run/react";
|
||||
+import { ServerRouter } from "react-router";
|
||||
|
||||
-<RemixServer context={remixContext} url={request.url} />,
|
||||
+<ServerRouter context={remixContext} url={request.url} />,
|
||||
```
|
||||
|
||||
```diff filename=app/entry.client.tsx
|
||||
-import { RemixBrowser } from "@remix-run/react";
|
||||
+import { HydratedRouter } from "react-router/dom";
|
||||
|
||||
hydrateRoot(
|
||||
document,
|
||||
<StrictMode>
|
||||
- <RemixBrowser />
|
||||
+ <HydratedRouter />
|
||||
</StrictMode>,
|
||||
);
|
||||
```
|
||||
|
||||
## 9. Update types for `AppLoadContext`
|
||||
|
||||
<docs-info>
|
||||
|
||||
If you were using `remix-serve` you can skip this step. This is only applicable if you were using a custom server in Remix v2.
|
||||
|
||||
</docs-info>
|
||||
|
||||
Since React Router can be used as both a React framework _and_ a stand-alone routing library, the `context` argument for `LoaderFunctionArgs` and `ActionFunctionArgs` is now optional and typed as `any` by default. You can register types for your load context to get type safety for your loaders and actions.
|
||||
|
||||
👉 **Register types for your load context**
|
||||
|
||||
Before you migrate to the new `Route.LoaderArgs` and `Route.ActionArgs` types, you can temporarily augment `LoaderFunctionArgs` and `ActionFunctionArgs` with your load context type to ease migration.
|
||||
|
||||
```ts filename=app/env.ts
|
||||
declare module "react-router" {
|
||||
// Your AppLoadContext used in v2
|
||||
interface AppLoadContext {
|
||||
whatever: string;
|
||||
}
|
||||
|
||||
// TODO: remove this once we've migrated to `Route.LoaderArgs` instead for our loaders
|
||||
interface LoaderFunctionArgs {
|
||||
context: AppLoadContext;
|
||||
}
|
||||
|
||||
// TODO: remove this once we've migrated to `Route.ActionArgs` instead for our actions
|
||||
interface ActionFunctionArgs {
|
||||
context: AppLoadContext;
|
||||
}
|
||||
}
|
||||
|
||||
export {}; // necessary for TS to treat this as a module
|
||||
```
|
||||
|
||||
<docs-info>
|
||||
|
||||
Using `declare module` to register types is a standard TypeScript technique called [module augmentation][ts-module-augmentation].
|
||||
You can do this in any TypeScript file covered by your `tsconfig.json`'s `include` field, but we recommend a dedicated `env.ts` within your app directory.
|
||||
|
||||
</docs-info>
|
||||
|
||||
👉 **Use the new types**
|
||||
|
||||
Once you adopt the [new type generation][type-safety], you can remove the `LoaderFunctionArgs`/`ActionFunctionArgs` augmentations and use the `context` argument from [`Route.LoaderArgs`][server-loaders] and [`Route.ActionArgs`][server-actions] instead.
|
||||
|
||||
```ts filename=app/env.ts
|
||||
declare module "react-router" {
|
||||
// Your AppLoadContext used in v2
|
||||
interface AppLoadContext {
|
||||
whatever: string;
|
||||
}
|
||||
}
|
||||
|
||||
export {}; // necessary for TS to treat this as a module
|
||||
```
|
||||
|
||||
```ts filename=app/routes/my-route.tsx
|
||||
import type { Route } from "./+types/my-route";
|
||||
|
||||
export function loader({ context }: Route.LoaderArgs) {}
|
||||
// { whatever: string } ^^^^^^^
|
||||
|
||||
export function action({ context }: Route.ActionArgs) {}
|
||||
// { whatever: string } ^^^^^^^
|
||||
```
|
||||
|
||||
Congratulations! You are now on React Router v7. Go ahead and run your application to make sure everything is working as expected.
|
||||
|
||||
[incremental-path-to-react-19]: https://remix.run/blog/incremental-path-to-react-19
|
||||
[v2-future-flags]: https://remix.run/docs/start/future-flags
|
||||
[routing]: ../start/framework/routing
|
||||
[fs-routing]: ../how-to/file-route-conventions
|
||||
[v7-changelog-types]: https://github.com/remix-run/react-router/blob/release-next/CHANGELOG.md#type-safety-improvements
|
||||
[server-loaders]: ../start/framework/data-loading#server-data-loading
|
||||
[server-actions]: ../start/framework/actions#server-actions
|
||||
[ts-module-augmentation]: https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
|
||||
[type-safety]: ../explanation/type-safety
|
||||
[codemod]: https://app.codemod.com/registry/remix/2/react-router/upgrade
|
||||
[jrestall]: https://github.com/jrestall
|
||||
Reference in New Issue
Block a user