Skip to content

Commit 193ea8a

Browse files
committed
Merge branch 'dev' into markdalgleish/handle-new-config-file
2 parents 5620c65 + af0acf1 commit 193ea8a

File tree

79 files changed

+1439
-372
lines changed

Some content is hidden

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

79 files changed

+1439
-372
lines changed

.changeset/five-cheetahs-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/node": patch
3+
---
4+
5+
Add createRequestListener to @react-router/node

README.md

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,28 @@
1-
# Welcome to React Router · [![npm package][npm-badge]][npm] [![build][build-badge]][build]
1+
[![npm package][npm-badge]][npm] [![build][build-badge]][build]
22

33
[npm-badge]: https://img.shields.io/npm/v/react-router-dom.svg?style=flat-square
44
[npm]: https://www.npmjs.org/package/react-router-dom
55
[build-badge]: https://img.shields.io/github/actions/workflow/status/remix-run/react-router/test.yml?branch=dev&style=square
66
[build]: https://github.com/remix-run/react-router/actions/workflows/test.yml
77

8-
React Router is a lightweight, fully-featured routing library for the [React](https://reactjs.org) JavaScript library. React Router runs anywhere React runs; on the web, on the server with node.js, or on any other Javascript platform that supports the [Web Fetch API][fetch-api].
8+
React Router is a multi-strategy router for React bridging the gap from React 18 to React 19. You can use it maximally as a React framework or minimally as a library with your own architecture.
99

10-
If you're new to React Router, we recommend you start with [the tutorial](https://reactrouter.com/en/main/start/tutorial).
11-
12-
If you're migrating to v6 from v5 (or v4, which is the same as v5), check out [the migration guide](/docs/upgrading/v5.md). If you're migrating from Reach Router, check out [the migration guide for Reach Router](/docs/upgrading/reach.md). If you need to find the code for v5, [it is on the `v5` branch](https://github.com/remix-run/react-router/tree/v5).
13-
14-
Documentation for v6 can be found [on our website](https://reactrouter.com/).
15-
16-
## Contributing
17-
18-
There are many different ways to contribute to React Router's development. If you're interested, check out [our contributing guidelines](CONTRIBUTING.md) to learn how you can get involved.
10+
- [Getting Started - Framework](https://reactrouter.com/start/framework/installation)
11+
- [Getting Started - Library](https://react.router.com/start/library/installation)
12+
- [Upgrade from v6](https://reactrouter.com/upgrading/v6)
13+
- [Upgrade from Remix](https://reactrouter.com/upgrading/remix)
14+
- [Changelog](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md)
1915

2016
## Packages
2117

22-
This repository is a monorepo containing the following packages:
23-
24-
- [`@react-router/dev`](/packages/react-router-dev)
25-
- [`@react-router/express`](/packages/react-router-express)
26-
- [`@react-router/node`](/packages/react-router-node)
27-
- [`@react-router/serve`](/packages/react-router-serve)
28-
- [`react-router`](/packages/react-router)
29-
- [`react-router-dom`](/packages/react-router-dom)
30-
31-
## Changes
32-
33-
Detailed release notes for a given version can be found [on our releases page](https://github.com/remix-run/react-router/releases).
34-
35-
## Funding
36-
37-
You may provide financial support for this project by donating [via Open Collective](https://opencollective.com/react-router). Thank you for your support!
38-
39-
## About
18+
- [react-router](./modules/react_router)
19+
- [@react-router/dev](./modules/_react_router_dev)
20+
- [@react-router/node](./modules/_react_router_node)
21+
- [@react-router/cloudflare](./modules/_react_router_cloudflare)
22+
- [@react-router/serve](./modules/_react_router_serve)
23+
- [@react-router/fs-routes](./modules/_react_router_fs_routes)
4024

41-
React Router is developed and maintained by [Remix Software](https://remix.run) and many [amazing contributors](https://github.com/remix-run/react-router/graphs/contributors).
25+
## Previous Versions
4226

43-
[fetch-api]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
27+
- [v6](https://reactrouter.com/v6)
28+
- [v5](https://v5.reactrouter.com/)

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@
256256
- thecode00
257257
- theostavrides
258258
- thepedroferrari
259+
- thethmuu
259260
- thisiskartik
260261
- thomasverleye
261262
- ThornWu
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# `react-router.config.ts`
2+
3+
Date: 2024-11-21
4+
5+
Status: accepted
6+
7+
## Context
8+
9+
Previously in Remix and earlier pre-releases of React Router, framework config was passed directly to the Vite plugin as an options object. While this has worked well so far when limited to Vite-specific use cases or simple CLI commands, we've started to run into some limitations as our `react-router` CLI has become more advanced.
10+
11+
Some key issues with the current approach:
12+
13+
1. **Tight coupling with Vite**
14+
15+
Our CLI commands (`react-router routes` and `react-router typegen`) need access to framework config but have nothing to do with Vite. We previously worked around this by using Vite to resolve `vite.config.ts` and then extracting our React Router config from the resolved Vite config object, but this approach proved to be difficult as we added more features to our CLI.
16+
17+
2. **Limited config watching capabilities**
18+
19+
The introduction of `react-router typegen --watch` in particular highlighted the limitations of our Vite-coupled approach. We needed to not only resolve our config but also watch for changes. Having this tied to the Vite config made implementing this functionality unnecessarily complex.
20+
21+
3. **Heavy-handed config updates**
22+
23+
Changes to Vite plugin options are treated like any other change to the Vite config, triggering a full reload of the dev server. This takes away any ability for us to handle config updates more gracefully.
24+
25+
4. **Difficulty with config documentation**
26+
27+
Documentation of our config options was difficult since we either had to show a complete Vite config file with a lot of extra noise, or only show a call to the `reactRouter` plugin which looked a bit confusing since it was labelled as a `vite.config.ts` file. Neither approach was ideal for clearly explaining our config options while keeping code snippets to a minimum.
28+
29+
## Goals
30+
31+
1. Decouple framework config from Vite
32+
2. Enable granular config watching for tools like `react-router typegen --watch`
33+
3. Avoid unnecessary dev server reloads when config changes
34+
4. Improve documentation by separating framework config from Vite config
35+
36+
## Decisions
37+
38+
### Introduce dedicated `react-router.config.ts` in the root of the project
39+
40+
We will introduce a dedicated config file, `react-router.config.ts/js`.
41+
42+
### Config is provided via a default export
43+
44+
To maintain consistency with other JS build tool configuration patterns, we will export the config object as the default export of the `react-router.config.ts` file.
45+
46+
### Change `app/routes.ts` API to use a default export rather than a named `routes` export
47+
48+
Now that we have multiple config files (`react-router.config.ts` and `app/routes.ts`), we should be internally consistent and use default exports for all of our config files. Now is a good time to make this change since the `routes.ts` API hasn't yet had a stable release.
49+
50+
### Any config APIs should be exported from `@react-router/dev/config`
51+
52+
The exported config object should satisfy the `Config` type from `@react-router/dev/config`. This follows our established pattern of using `@react-router/dev/*` namespaces for dev-time APIs that are scoped to particular files, e.g. `@react-router/dev/routes` and `@react-router/dev/vite`.
53+
54+
### Config file is optional but recommended
55+
56+
While the lack of a config file won't be treated as an error, we should include a blank config file in all official templates to make the config options more discoverable and self-documenting.
57+
58+
### Remove options from Vite plugin
59+
60+
The Vite plugin will no longer accept config options. All framework options will be handled through the dedicated config file.
61+
62+
### Improved config update handling
63+
64+
Config changes should no longer trigger full dev server reloads. We may re-introduce this behavior in certain cases where it makes sense.

docs/explanation/code-splitting.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ When using React Router's framework features, your application is automatically
1010

1111
Consider this simple route config:
1212

13-
```tsx filename=routes.ts
13+
```tsx filename=app/routes.ts
14+
import {
15+
type RouteConfig,
16+
route,
17+
} from "@react-router/dev/routes";
18+
1419
export default [
1520
route("/contact", "./contact.tsx"),
1621
route("/about", "./about.tsx"),
17-
];
22+
] satisfies RouteConfig;
1823
```
1924

2025
Instead of bundling all routes into a single giant build, the modules referenced (`contact.tsx` and `about.tsx`) become entry points to the bundler.
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
title: Hot Module Replacement
3+
---
4+
5+
# Hot Module Replacement
6+
7+
Hot Module Replacement is a technique for updating modules in your app without needing to reload the page.
8+
It's a great developer experience, and React Router supports it when using Vite.
9+
10+
HMR does its best to preserve browser state across updates.
11+
For example, let's say you have form within a modal and you fill out all the fields.
12+
As soon as you save any changes to the code, traditional live reload would hard refresh the page causing all of those fields to be reset.
13+
Every time you make a change, you'd have to open up the modal _again_ and fill out the form _again_.
14+
15+
But with HMR, all of that state is preserved _across updates_.
16+
17+
## React Fast Refresh
18+
19+
React already has mechanisms for updating the DOM via its [virtual DOM][virtual-dom] in response to user interactions like clicking a button.
20+
Wouldn't it be great if React could handle updating the DOM in response to code changes too?
21+
22+
That's exactly what [React Fast Refresh][react-refresh] is all about!
23+
Of course, React is all about components, not general JavaScript code, so React Fast Refresh only handles hot updates for exported React components.
24+
25+
But React Fast Refresh does have some limitations that you should be aware of.
26+
27+
### Class Component State
28+
29+
React Fast Refresh does not preserve state for class components.
30+
This includes higher-order components that internally return classes:
31+
32+
```tsx
33+
export class ComponentA extends Component {} //
34+
35+
export const ComponentB = HOC(ComponentC); // ❌ Won't work if HOC returns a class component
36+
37+
export function ComponentD() {} //
38+
export const ComponentE = () => {}; //
39+
export default function ComponentF() {} //
40+
```
41+
42+
### Named Function Components
43+
44+
Function components must be named, not anonymous, for React Fast Refresh to track changes:
45+
46+
```tsx
47+
export default () => {}; //
48+
export default function () {} //
49+
50+
const ComponentA = () => {};
51+
export default ComponentA; //
52+
53+
export default function ComponentB() {} //
54+
```
55+
56+
### Supported Exports
57+
58+
React Fast Refresh can only handle component exports. While React Router manages [route exports like `action`, ` headers`, `links`, `loader`, and `meta`][route-module] for you, any user-defined exports will cause full reloads:
59+
60+
```tsx
61+
// These exports are handled by the React Router Vite plugin
62+
// to be HMR-compatible
63+
export const meta = { title: "Home" }; //
64+
export const links = [
65+
{ rel: "stylesheet", href: "style.css" },
66+
]; //
67+
68+
// These exports are removed by the React Router Vite plugin
69+
// so they never affect HMR
70+
export const headers = { "Cache-Control": "max-age=3600" }; //
71+
export const loader = async () => {}; //
72+
export const action = async () => {}; //
73+
74+
// This is not a route module export, nor a component export,
75+
// so it will cause a full reload for this route
76+
export const myValue = "some value"; //
77+
78+
export default function Route() {} //
79+
```
80+
81+
👆 Routes probably shouldn't be exporting random values like that anyway.
82+
If you want to reuse values across routes, stick them in their own non-route module:
83+
84+
```ts filename=my-custom-value.ts
85+
export const myValue = "some value";
86+
```
87+
88+
### Changing Hooks
89+
90+
React Fast Refresh cannot track changes for a component when hooks are being added or removed from it, causing full reloads just for the next render. After the hooks have been updated, changes should result in hot updates again. For example, if you add a `useState` to your component, you may lose that component's local state for the next render.
91+
92+
Additionally, if you are destructuring a hook's return value, React Fast Refresh will not be able to preserve state for the component if the destructured key is removed or renamed.
93+
For example:
94+
95+
```tsx
96+
export default function Component({ loaderData }) {
97+
const { pet } = useMyCustomHook();
98+
return (
99+
<div>
100+
<input />
101+
<p>My dog's name is {pet.name}!</p>
102+
</div>
103+
);
104+
}
105+
```
106+
107+
If you change the key `pet` to `dog`:
108+
109+
```diff
110+
export default function Component() {
111+
- const { pet } = useMyCustomHook();
112+
+ const { dog } = useMyCustomHook();
113+
return (
114+
<div>
115+
<input />
116+
- <p>My dog's name is {pet.name}!</p>
117+
+ <p>My dog's name is {dog.name}!</p>
118+
</div>
119+
);
120+
}
121+
```
122+
123+
then React Fast Refresh will not be able to preserve state `<input />` ❌.
124+
125+
### Component Keys
126+
127+
In some cases, React cannot distinguish between existing components being changed and new components being added. [React needs `key`s][react-keys] to disambiguate these cases and track changes when sibling elements are modified.
128+
129+
[virtual-dom]: https://reactjs.org/docs/faq-internals.html#what-is-the-virtual-dom
130+
[react-refresh]: https://github.com/facebook/react/tree/main/packages/react-refresh
131+
[react-keys]: https://react.dev/learn/rendering-lists#why-does-react-need-keys
132+
[route-module]: ../start/framework/route-module

docs/explanation/type-safety.md

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Type Safety
44

55
# Type Safety
66

7-
If you haven't done so already, check out our guide for <a href="../framework/how-to/setting-up-type-safety">setting up type safety</a> in a new project.
7+
If you haven't done so already, check out our guide for [setting up type safety][route-module-type-safety] in a new project.
88

99
React Router generates types for each route in your app that you can use to get type safety for each route module export.
1010

@@ -39,6 +39,16 @@ export default function Component({
3939
}
4040
```
4141

42+
## How it works
43+
44+
React Router's type generation executes your route config (`app/routes.ts` by default) to determine the routes for your app.
45+
It then generates a `+types/<route file>.d.ts` for each route within a special `.react-router/types/` directory.
46+
With [`rootDirs` configured][route-module-type-safety], TypeScript can import these generated files as if they were right next to their corresponding route modules.
47+
48+
For a deeper dive into some of the design decisions, check out our [type inference decision doc](https://github.com/remix-run/react-router/blob/dev/decisions/0012-type-inference.md).
49+
50+
[route-module-type-safety]: ../how-to/route-module-type-safety
51+
4252
## `typegen` command
4353

4454
You can manually generate types with the `typegen` command:
@@ -47,12 +57,6 @@ You can manually generate types with the `typegen` command:
4757
react-router typegen
4858
```
4959

50-
You can also use `--watch` to automatically regenerate types as files change:
51-
52-
```sh
53-
react-router typegen --watch
54-
```
55-
5660
The following types are generated for each route:
5761

5862
- `LoaderArgs`
@@ -63,12 +67,11 @@ The following types are generated for each route:
6367
- `ComponentProps` (for the `default` export)
6468
- `ErrorBoundaryProps`
6569

66-
## How it works
67-
68-
React Router's type generation executes your route config (`app/routes.ts` by default) to determine the routes for your app.
69-
It then generates a `+types/<route file>.d.ts` for each route within a special `.react-router/types/` directory.
70-
With [`rootDirs` configured][setting-up-type-safety], TypeScript can import these generated files as if they were right next to their corresponding route modules.
70+
### --watch
7171

72-
For a deeper dive into some of the design decisions, check out our [type inference decision doc](https://github.com/remix-run/react-router/blob/dev/decisions/0012-type-inference.md).
72+
If you run `react-router dev` — or if your custom server calls `vite.createServer` — then React Router's Vite plugin is already generating up-to-date types for you.
73+
But if you really need to run type generation on its own, you can also use `--watch` to automatically regenerate types as files change:
7374

74-
[setting-up-type-safety]: ../framework/how-to/setting-up-type-safety
75+
```sh
76+
react-router typegen --watch
77+
```

0 commit comments

Comments
 (0)