Skip to content

Commit d0ab074

Browse files
committed
Merge branch 'dev' into release-next
2 parents 51fb9a6 + 8f44d01 commit d0ab074

File tree

58 files changed

+1157
-472
lines changed

Some content is hidden

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

58 files changed

+1157
-472
lines changed

.changeset/little-cooks-pull.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Fix typegen for routes with a client loader but no server loader

.changeset/rare-plums-chew.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@react-router/serve": patch
3+
"@react-router/dev": patch
4+
"react-router": patch
5+
---
6+
7+
- Fix `react-router-serve` handling of prerendered HTML files by removing the `redirect: false` option so it now falls back on the default `redirect: true` behavior of redirecting from `/folder` -> `/folder/` which will then pick up `/folder/index.html` from disk. See https://expressjs.com/en/resources/middleware/serve-static.html
8+
- Proxy prerendered loader data into prerender pass for HTML files to avoid double-invocations of the loader at build time

.changeset/three-seals-play.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": minor
3+
---
4+
5+
Add `prefix` route config helper to `@react-router/dev/routes`

contributors.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- andreiduca
2323
- antonmontrezor
2424
- appden
25+
- apple-yagi
2526
- arjunyel
2627
- arka1002
2728
- Armanio
@@ -207,6 +208,7 @@
207208
- OlegDev1
208209
- omahs
209210
- omar-moquete
211+
- OnurGvnc
210212
- p13i
211213
- parched
212214
- parveen232

docs/misc/pre-rendering.md

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ If you pre-render all of the paths in your application, you can deploy your `bui
9292

9393
### Serving via react-router-serve
9494

95-
By default, `react-router-serve` will serve these files via [`express.static`][express-static] and any paths that do not match a static file will fall through to the Remix handler.
95+
By default, `react-router-serve` will serve these files via [`express.static`][express-static] and any paths that do not match a static file will fall through to the React Router handler.
9696

9797
This even allows you to run a hybrid setup where _some_ of your routes are pre-rendered and others are dynamically rendered at runtime. For example, you could prerender anything inside `/blog/*` and server-render anything inside `/auth/*`.
9898

@@ -111,19 +111,7 @@ app.use(
111111
);
112112

113113
// Serve static HTML and .data requests without Cache-Control
114-
app.use(
115-
"/",
116-
express.static("build/client", {
117-
// Don't redirect directory index.html requests to include a trailing slash
118-
redirect: false,
119-
setHeaders: function (res, path) {
120-
// Add the proper Content-Type for turbo-stream data responses
121-
if (path.endsWith(".data")) {
122-
res.set("Content-Type", "text/x-turbo");
123-
}
124-
},
125-
})
126-
);
114+
app.use("/", express.static("build/client"));
127115

128116
// Serve remaining unhandled requests via your React Router handler
129117
app.all(

docs/start/routing.md

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ Routes are configured in `app/routes.ts`. Routes have a url pattern to match the
2424
import { route } from "@react-router/dev/routes";
2525

2626
export const routes = [
27-
route("some/path", "./some/file.tsx");
27+
route("some/path", "./some/file.tsx"),
2828
// pattern ^ ^ module file
29-
]
29+
];
3030
```
3131

3232
Here is a larger sample route config:
@@ -37,6 +37,7 @@ import {
3737
route,
3838
index,
3939
layout,
40+
prefix,
4041
} from "@react-router/dev/routes";
4142

4243
export const routes: RouteConfig = [
@@ -48,7 +49,7 @@ export const routes: RouteConfig = [
4849
route("register", "./auth/register.tsx"),
4950
]),
5051

51-
route("concerts", [
52+
...prefix("concerts", [
5253
index("./concerts/home.tsx"),
5354
route(":city", "./concerts/city.tsx"),
5455
route("trending", "./concerts/trending.tsx"),
@@ -63,7 +64,7 @@ If you prefer to define your routes via file naming conventions rather than conf
6364
The files referenced in `routes.ts` define each route's behavior:
6465

6566
```tsx filename=app/routes.ts
66-
route("teams/:teamId", "./team.tsx");
67+
route("teams/:teamId", "./team.tsx"),
6768
// route module ^^^^^^^^
6869
```
6970

@@ -83,7 +84,7 @@ export async function loader({ params }: Route.LoaderArgs) {
8384
export default function Component({
8485
loaderData,
8586
}: Route.ComponentProps) {
86-
return <h1>{data.name}</h1>;
87+
return <h1>{loaderData.name}</h1>;
8788
}
8889
```
8990

@@ -136,20 +137,21 @@ Every route in `routes.ts` is nested inside the special `app/root.tsx` module.
136137

137138
Using `layout`, layout routes create new nesting for their children, but they don't add any segments to the URL. It's like the root route but they can be added at any level.
138139

139-
```tsx filename=app/routes.ts lines=[9,15]
140+
```tsx filename=app/routes.ts lines=[10,16]
140141
import {
141142
type RouteConfig,
142143
route,
143144
layout,
144145
index,
146+
prefix,
145147
} from "@react-router/dev/routes";
146148

147149
export const routes: RouteConfig = [
148150
layout("./marketing/layout.tsx", [
149151
index("./marketing/home.tsx"),
150152
route("contact", "./marketing/contact.tsx"),
151153
]),
152-
route("projects", [
154+
...prefix("projects", [
153155
index("./projects/home.tsx"),
154156
layout("./projects/project-layout.tsx", [
155157
route(":pid", "./projects/project.tsx"),
@@ -162,7 +164,7 @@ export const routes: RouteConfig = [
162164
## Index Routes
163165

164166
```ts
165-
index(componentFile);
167+
index(componentFile),
166168
```
167169

168170
Index routes render into their parent's [Outlet][outlet] at their parent's URL (like a default child route).
@@ -187,19 +189,47 @@ export const routes: RouteConfig = [
187189

188190
Note that index routes can't have children.
189191

192+
## Route Prefixes
193+
194+
Using `prefix`, you can add a path prefix to a set of routes without needing to introduce a parent route file.
195+
196+
```tsx filename=app/routes.ts lines=[14]
197+
import {
198+
type RouteConfig,
199+
route,
200+
layout,
201+
index,
202+
prefix,
203+
} from "@react-router/dev/routes";
204+
205+
export const routes: RouteConfig = [
206+
layout("./marketing/layout.tsx", [
207+
index("./marketing/home.tsx"),
208+
route("contact", "./marketing/contact.tsx"),
209+
]),
210+
...prefix("projects", [
211+
index("./projects/home.tsx"),
212+
layout("./projects/project-layout.tsx", [
213+
route(":pid", "./projects/project.tsx"),
214+
route(":pid/edit", "./projects/edit-project.tsx"),
215+
]),
216+
]),
217+
];
218+
```
219+
190220
## Dynamic Segments
191221

192222
If a path segment starts with `:` then it becomes a "dynamic segment". When the route matches the URL, the dynamic segment will be parsed from the URL and provided as `params` to other router APIs.
193223

194224
```ts filename=app/routes.ts
195-
route("teams/:teamId", "./team.tsx");
225+
route("teams/:teamId", "./team.tsx"),
196226
```
197227

198228
```tsx filename=app/team.tsx
199229
import type * as Route from "./+types.team";
200230

201-
async function loader({ params }: Route.LoaderArgs) {
202-
// ^? { teamId: string }
231+
export async function loader({ params }: Route.LoaderArgs) {
232+
// ^? { teamId: string }
203233
}
204234

205235
export default function Component({
@@ -213,7 +243,7 @@ export default function Component({
213243
You can have multiple dynamic segments in one route path:
214244

215245
```ts filename=app/routes.ts
216-
route("c/:categoryId/p/:productId", "./product.tsx");
246+
route("c/:categoryId/p/:productId", "./product.tsx"),
217247
```
218248

219249
```tsx filename=app/product.tsx
@@ -229,7 +259,7 @@ async function loader({ params }: LoaderArgs) {
229259
You can make a route segment optional by adding a `?` to the end of the segment.
230260

231261
```ts filename=app/routes.ts
232-
route(":lang?/categories", "./categories.tsx");
262+
route(":lang?/categories", "./categories.tsx"),
233263
```
234264

235265
You can have optional static segments, too:
@@ -243,7 +273,7 @@ route("users/:userId/edit?", "./user.tsx");
243273
Also known as "catchall" and "star" segments. If a route path pattern ends with `/*` then it will match any characters following the `/`, including other `/` characters.
244274

245275
```ts filename=app/routes.ts
246-
route("files/*", "./files.tsx");
276+
route("files/*", "./files.tsx"),
247277
```
248278

249279
```tsx filename=app/files.tsx

docs/upgrading/remix.md

Lines changed: 113 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,125 @@ hidden: true
55

66
# Upgrading from Remix
77

8-
<docs-warning>This guide is still in development</docs-warning>
8+
<docs-warning>This guide is still in development and is subject to change as React Router stabilizes prior to the `7.0.0` stable release</docs-warning>
99

10-
After the final React Router v7 release, we will go back to Remix to add future flags to any changed APIs.
10+
Our intention is for the **Remix v2 -> React Router v7** upgrade path to be as non-breaking as possible via the use of [Future Flags][future-flags] and codemods for minor and straightforward code adjustments. To best prepare for this eventual upgrade, you can start by adopting all of the existing [Remix v2 Future Flags][v2-future-flags].
1111

12-
If you want to attempt the rocky migration now, the following table will be helpful:
12+
## Upgrading to the v7 Prerelease
1313

14-
| Remix v2 Package | | React Router v7 Package |
15-
| ----------------------- | --- | -------------------------- |
16-
| `@remix-run/react` | ➡️ | `react-router` |
17-
| `@remix-run/dev` | ➡️ | `@react-router/dev` |
18-
| `@remix-run/node` | ➡️ | `@react-router/node` |
19-
| `@remix-run/cloudflare` | ➡️ | `@react-router/cloudflare` |
14+
If you want to attempt the (potentially rocky) migration now, the following steps should get you most of the way there. If you run into issues please let us know in [Discord][remix-discord] or [Github][github-new-issue].
2015

21-
Also note that nearly all modules your app needs come from `react-router` now instead of `@remix-run/node` and `@remix-run/cloudflare`, so try to import from there first.
16+
### Step 1 - Adopt future flags
17+
18+
Adopt all existing [future flags][v2-future-flags] in your Remix v2 application.
19+
20+
### Step 2 - Update dependencies
21+
22+
You'll need to update your dependencies from the `@remix-run/*` packages to `react-router` and `@react-router/*` packages in `package.json` and in your code where you import from packages:
23+
24+
| Remix v2 Package | | React Router v7 Package |
25+
| --------------------------- | --- | -------------------------- |
26+
| `@remix-run/architect` | ➡️ | `@react-router/architect` |
27+
| `@remix-run/cloudflare` | ➡️ | `@react-router/cloudflare` |
28+
| `@remix-run/dev` | ➡️ | `@react-router/dev` |
29+
| `@remix-run/express` | ➡️ | `@react-router/express` |
30+
| `@remix-run/node` | ➡️ | `@react-router/node` |
31+
| `@remix-run/react` | ➡️ | `react-router` |
32+
| `@remix-run/serve` | ➡️ | `@react-router/serve` |
33+
| `@remix-run/server-runtime` | ➡️ | `react-router` |
34+
| `@remix-run/testing` | ➡️ | `react-router` |
35+
36+
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`.
2237

2338
```diff
2439
-import { redirect } from "@react-router/node";
2540
+import { redirect } from "react-router";
2641
```
42+
43+
The only APIs 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.
44+
45+
### Step 3 - Change `scripts` in `package.json`
46+
47+
Update the scripts in your `package.json`:
48+
49+
| Script | Remix v2 | | React Router v7 |
50+
| ----------- | ----------------------------------- | --- | ------------------------------------------ |
51+
| `dev` | `remix vite:dev` | ➡️ | `react-router dev` |
52+
| `build` | `remix vite:build` | ➡️ | `react-router build` |
53+
| `start` | `remix-serve build/server/index.js` | ➡️ | `react-router-serve build/server/index.js` |
54+
| `typecheck` | `tsc` | ➡️ | `react-router typegen && tsc` |
55+
56+
### Step 4 - Rename plugin in `vite.config`
57+
58+
Update the import and rename the plugin in your `vite.config.ts`:
59+
60+
```diff
61+
-import { vitePlugin as remix } from "@remix-run/dev";
62+
+import { reactRouter } from "@react-router/dev/vite";
63+
import { defineConfig } from "vite";
64+
import tsconfigPaths from "vite-tsconfig-paths";
65+
66+
export default defineConfig({
67+
plugins: [
68+
- remix({
69+
- future: {
70+
- // all future flags adopted
71+
- },
72+
- }),
73+
+ reactRouter(),
74+
tsconfigPaths(),
75+
],
76+
});
77+
```
78+
79+
### Step 5 - Add a `routes.ts` file
80+
81+
In React Router v7 you define your routes using the [`app/routes.ts`][routing] file. For backwards-compatibility and for folks who prefer [file-based conventions][fs-routing], you can opt-into the same "flat routes" convention you are using in Remix v2 via the new `@react-router/fs-routes` package:
82+
83+
```ts filename=app/routes.ts
84+
import { type RouteConfig } from "@react-router/dev/routes";
85+
import { flatRoutes } from "@react-router/fs-routes";
86+
87+
export const routes: RouteConfig = flatRoutes();
88+
```
89+
90+
### Step 6 - Rename components in entry files
91+
92+
If you have an `entry.server.tsx` and/or an `entry.client.tsx` file in your application, you will need to rename the main components in this files:
93+
94+
| Entry File | Remix v2 Component | | React Router v7 Component |
95+
| ------------------ | ------------------ | --- | ------------------------- |
96+
| `entry.server.tsx` | `<RemixServer>` | ➡️ | `<ServerRouter>` |
97+
| `entry.client.stx` | `<RemixBrowser>` | ➡️ | `<HydratedRouter>` |
98+
99+
## Known Prerelease Issues
100+
101+
### Typesafety
102+
103+
We have introduced some major changes to improve the type story in v7, but we're still working on making sure the path to adopt them is as smooth as possible prior to a stable v7 release. You can read more about the new type story in the [v7 draft release notes][v7-changelog-types] and if it's not a huge lift, your best bet for types in v7 is to migrate to that approach across the board.
104+
105+
For the time being we don't have a great story to _incrementally_ migrate data types to the v7 prerelease. We never brought the generics on data APIs (`useLoaderData`, `useFetcher`, `Await`, etc.) over from Remix to React Router because we knew that we could do better than what Remix v2 had, and we wanted to ensure that we didn't ship APIs in React Router just to yank them out. Now that we have a better idea of the type story in React Router v7, we're better able to see what the migration path looks like and we plan on shipping improvements in this area in an upcoming v7 prerelease.
106+
107+
Currently, when you upgrade to React Router v7 you're going to get typescript yelling at you a bunch for these missing generics that existed in your Remix v2 app code. For now, you have 2 options to continue testing out the prerelease:
108+
109+
**Option 1 - Ignore the type errors with `@ts-expect-error` or `@ts-ignore`**
110+
111+
```diff
112+
+// @ts-expect-error
113+
let data = useLoaderData<typeof loader>();
114+
```
115+
116+
**Option 2 - Remove the generics and cast the types manually**
117+
118+
```diff
119+
-let data = useLoaderData<typeof loader>();
120+
+let data = useLoaderData() as ReturnType<Awaited<typeof loader>>;
121+
```
122+
123+
[future-flags]: ../community/api-development-strategy
124+
[v2-future-flags]: https://remix.run/docs/start/future-flags
125+
[remix-discord]: https://rmx.as/discord
126+
[github-new-issue]: https://github.com/remix-run/react-router/issues/new/choose
127+
[routing]: ../start/routing
128+
[fs-routing]: ../misc/file-route-conventions
129+
[v7-changelog-types]: https://github.com/remix-run/react-router/blob/release-next/CHANGELOG.md#typesafety-improvements

docs/upgrading/vite-router-provider.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ The first few routes you migrate are the hardest because you often have to acces
230230

231231
## Enable SSR and/or Pre-rendering
232232

233-
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. For SSR you'll need to also deploy the server build to a server. See [Deploying](./deploying) for more information.
233+
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. For SSR you'll need to also deploy the server build to a server. See [Deploying](../start/deploying) for more information.
234234

235235
```ts filename=vite.config.ts
236236
import { reactRouter } from "@react-router/dev/vite";

0 commit comments

Comments
 (0)