├── app
├── routes
│ ├── $.tsx
│ ├── page3.tsx
│ └── index.tsx
├── entry.client.tsx
├── old-app
│ ├── pages
│ │ ├── page-4
│ │ │ └── index.js
│ │ └── page-2
│ │ │ └── index.js
│ └── app.js
├── entry.server.tsx
└── root.tsx
├── .gitignore
├── .eslintrc
├── remix.env.d.ts
├── public
└── favicon.ico
├── remix.config.js
├── tsconfig.json
├── package.json
└── README.md
/app/routes/$.tsx:
--------------------------------------------------------------------------------
1 | export { default } from "~/old-app/app";
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | /.cache
4 | /build
5 | /public/build
6 | .env
7 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@remix-run/eslint-config", "@remix-run/eslint-config/node"]
3 | }
4 |
--------------------------------------------------------------------------------
/remix.env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kentcdodds/incremental-react-router-to-remix-upgrade-path/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/app/entry.client.tsx:
--------------------------------------------------------------------------------
1 | import { RemixBrowser } from "@remix-run/react";
2 | import { hydrate } from "react-dom";
3 |
4 | hydrate(, document);
5 |
--------------------------------------------------------------------------------
/remix.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('@remix-run/dev').AppConfig}
3 | */
4 | module.exports = {
5 | ignoredRouteFiles: ["**/.*"],
6 | // appDirectory: "app",
7 | // assetsBuildDirectory: "public/build",
8 | // serverBuildPath: "build/index.js",
9 | // publicPath: "/build/",
10 | };
11 |
--------------------------------------------------------------------------------
/app/old-app/pages/page-4/index.js:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | export function PageFour() {
4 | return (
5 |
6 |
7 | Page 4
8 |
9 | Go Home (remix)
10 |
11 | Go to page 2 (old)
12 |
13 | Go to page 3 (remix)
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/app/routes/page3.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "@remix-run/react";
2 |
3 | export default function Page3() {
4 | return (
5 |
6 |
7 | Page 3 (remix)
8 |
9 | Go home (remix)
10 |
11 | Go to page 3 (remix)
12 |
13 | Go to page 4 (old)
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/app/old-app/pages/page-2/index.js:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | export function PageTwo() {
4 | return (
5 |
6 |
7 | Page 2 (old)
8 |
9 | Go home (remix)
10 |
11 | Go to page 3 (remix)
12 |
13 | Go to page 4 (old)
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/app/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "@remix-run/react";
2 |
3 | export default function Page3() {
4 | return (
5 |
6 |
7 | Home (remix)
8 |
9 | Go to page 2 (old)
10 |
11 | Go to page 3 (remix)
12 |
13 | Go to page 4 (old)
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/app/old-app/app.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { Routes, Route } from "react-router-dom";
4 | import { PageFour } from "./pages/page-4";
5 | import { PageTwo } from "./pages/page-2";
6 |
7 | function App() {
8 | return (
9 |
10 |
Old app
11 |
12 | } />
13 | } />
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
3 | "compilerOptions": {
4 | "lib": ["DOM", "DOM.Iterable", "ES2019"],
5 | "isolatedModules": true,
6 | "esModuleInterop": true,
7 | "jsx": "react-jsx",
8 | "moduleResolution": "node",
9 | "resolveJsonModule": true,
10 | "target": "ES2019",
11 | "strict": true,
12 | "baseUrl": ".",
13 | "paths": {
14 | "~/*": ["./app/*"]
15 | },
16 | "noEmit": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "allowJs": true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/entry.server.tsx:
--------------------------------------------------------------------------------
1 | import type { EntryContext } from "@remix-run/node";
2 | import { RemixServer } from "@remix-run/react";
3 | import { renderToString } from "react-dom/server";
4 |
5 | export default function handleRequest(
6 | request: Request,
7 | responseStatusCode: number,
8 | responseHeaders: Headers,
9 | remixContext: EntryContext
10 | ) {
11 | let markup = renderToString(
12 |
13 | );
14 |
15 | responseHeaders.set("Content-Type", "text/html");
16 |
17 | return new Response("" + markup, {
18 | status: responseStatusCode,
19 | headers: responseHeaders,
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/app/root.tsx:
--------------------------------------------------------------------------------
1 | import type { MetaFunction } from "@remix-run/node";
2 | import {
3 | Links,
4 | LiveReload,
5 | Meta,
6 | Outlet,
7 | Scripts,
8 | ScrollRestoration,
9 | } from "@remix-run/react";
10 |
11 | export const meta: MetaFunction = () => ({
12 | charset: "utf-8",
13 | title: "New Remix App",
14 | viewport: "width=device-width,initial-scale=1",
15 | });
16 |
17 | export default function App() {
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "remix-template-remix",
3 | "private": true,
4 | "description": "",
5 | "license": "",
6 | "sideEffects": false,
7 | "scripts": {
8 | "build": "remix build",
9 | "dev": "remix dev",
10 | "start": "remix-serve build"
11 | },
12 | "dependencies": {
13 | "@remix-run/node": "^1.4.3",
14 | "@remix-run/react": "^1.4.3",
15 | "@remix-run/serve": "^1.4.3",
16 | "react": "^17.0.2",
17 | "react-dom": "^17.0.2",
18 | "react-router-dom": "^6.3.0"
19 | },
20 | "devDependencies": {
21 | "@remix-run/dev": "^1.4.3",
22 | "@remix-run/eslint-config": "^1.4.3",
23 | "@types/react": "^17.0.24",
24 | "@types/react-dom": "^17.0.9",
25 | "eslint": "^8.11.0",
26 | "typescript": "^4.5.5"
27 | },
28 | "engines": {
29 | "node": ">=14"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Router + Remix
2 |
3 | This is an example of how you can incrementally migrate to Remix if you're using React Router. Here's the basic idea:
4 |
5 | 1. Upgrade your React Router site to the latest version of React Router ([find out how to do this iteratively](https://www.npmjs.com/package/react-router-dom-v5-compat)).
6 | 2. Install Remix and set up the conventional files of `app/{root,entry.client,entry.server}.tsx`
7 | 3. Move all your existing code into a directory within the `app` directory (like `app/old-app` for example).
8 | 4. Remove `` from your `App`
9 | 5. Create a `app/routes/$.tsx` file with just `export { default } from "~/old-app/app";` (or whatever file has the root component for your existing React Router app).
10 | 6. Remove all your old webpack build stuff and use the `remix` CLI instead. Your builds are now outrageously fast.
11 | 7. Everything should work at this point (_and_ it'll be server rendered too!!). Commit + push!
12 | 8. Over time, move old routes to the `routes` directory until you bring everything over.
13 | 9. You're done!
14 |
15 | We'll have better docs and even videos about this in the future, but this is a pretty darn solid and iterative approach with quick wins and a clear path.
16 |
--------------------------------------------------------------------------------