Skip to content

Commit 60ef15d

Browse files
committed
Merge branch 'dev' into jameso/add-helper-environment
2 parents 1f6b828 + 1e6b2e1 commit 60ef15d

35 files changed

+1007
-669
lines changed

.changeset/dull-balloons-boil.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"@react-router/dev": patch
3+
"react-router": patch
4+
---
5+
6+
Fix typegen for repeated params
7+
8+
In React Router, path parameters are keyed by their name.
9+
So for a path pattern like `/a/:id/b/:id?/c/:id`, the last `:id` will set the value for `id` in `useParams` and the `params` prop.
10+
For example, `/a/1/b/2/c/3` will result in the value `{ id: 3 }` at runtime.
11+
12+
Previously, generated types for params incorrectly modeled repeated params with an array.
13+
So `/a/1/b/2/c/3` generated a type like `{ id: [1,2,3] }`.
14+
15+
To be consistent with runtime behavior, the generated types now correctly model the "last one wins" semantics of path parameters.
16+
So `/a/1/b/2/c/3` now generates a type like `{ id: 3 }`.

.changeset/khaki-rocks-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
Fix `ArgError: unknown or unexpected option: --version` when running `react-router --version`

.changeset/late-nails-hear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
Skip action-only resource routes when using `prerender:true`

.changeset/rotten-numbers-bathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
Limit prerendered resource route `.data` files to only the target route

.changeset/slow-coats-arrive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
Lazy load Cloudflare platform proxy on first dev server request when using the `cloudflareDevProxy` Vite plugin to avoid creating unnecessary workerd processes

.changeset/sour-avocados-lick.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+
Add `unstable_SerializesTo` brand type for library authors to register types serializable by React Router's streaming format (`turbo-stream`)

.changeset/three-eyes-flow.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
"@react-router/dev": minor
3+
"react-router": minor
4+
---
5+
6+
New type-safe `href` utility that guarantees links point to actual paths in your app
7+
8+
```tsx
9+
import { href } from "react-router";
10+
11+
export default function Component() {
12+
const link = href("/blog/:slug", { slug: "my-first-post" });
13+
return (
14+
<main>
15+
<Link to={href("/products/:id", { id: "asdf" })} />
16+
<NavLink to={href("/:lang?/about", { lang: "en" })} />
17+
</main>
18+
);
19+
}
20+
```

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@
245245
- pcattori
246246
- petersendidit
247247
- phildl
248+
- phryneas
248249
- pierophp
249250
- printfn
250251
- promet99

docs/how-to/pre-rendering.md

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,45 @@ title: Pre-Rendering
44

55
# Pre-Rendering
66

7-
Pre-rendering allows you to render pages at build time instead of on a runtime server to speed up page loads for static content.
7+
Pre-Rendering allows you to speed up page loads for static content by rendering pages at build time instead of at runtime. Pre-rendering is enabled via the `prerender` config in `react-router.config.ts` and can be used in two ways based on the `ssr` config value:
88

9-
In some cases, you'll serve these pages _alongside_ a runtime SSR server. If you wish to pre-render pages and deploy them _without_ a runtime SSR server, please see the [Pre-rendering with `ssr:false`](#pre-rendering-without-a-runtime-ssr-server) section below.
9+
- Alongside a runtime SSR server ith `ssr:true` (the default value)
10+
- Deployed to a static file server with `ssr:false`
1011

11-
## Pre-rendering alongside a runtime SSR server
12+
## Pre-rendering with `ssr:true`
1213

1314
### Configuration
1415

1516
Add the `prerender` option to your config, there are three signatures:
1617

17-
```ts filename=react-router.config.ts lines=[7-09,11-12,14-20]
18+
```ts filename=react-router.config.ts lines=[7-8,10-11,13-21]
1819
import type { Config } from "@react-router/dev/config";
1920

2021
export default {
2122
// Can be omitted - defaults to true
2223
ssr: true,
2324

24-
// all static route paths
25-
// (no dynamic segments like "/post/:slug")
25+
// all static paths (no dynamic segments like "/post/:slug")
2626
prerender: true,
2727

28-
// any url
28+
// specific paths
2929
prerender: ["/", "/blog", "/blog/popular-post"],
3030

3131
// async function for dependencies like a CMS
32-
async pre-render({ getStaticPaths }) {
32+
async prerender({ getStaticPaths }) {
3333
let posts = await fakeGetPostsFromCMS();
34-
return ["/", "/blog"].concat(
35-
posts.map((post) => post.href)
36-
);
34+
return [
35+
"/",
36+
"/blog",
37+
...posts.map((post) => post.href),
38+
];
3739
},
3840
} satisfies Config;
3941
```
4042

4143
### Data Loading and Pre-rendering
4244

43-
There is no extra application API for pre-rendering. Pre-rendering uses the same route loaders as server rendering:
45+
There is no extra application API for pre-rendering. Routes being pre-rendered use the same route `loader` functions as server rendering:
4446

4547
```tsx
4648
export async function loader({ request, params }) {
@@ -82,18 +84,18 @@ Prerender: Generated build/client/blog/my-first-post/index.html
8284

8385
During development, pre-rendering doesn't save the rendered results to the public directory, this only happens for `react-router build`.
8486

85-
## Pre-rendering without a runtime SSR server
87+
## Pre-rendering with `ssr:false`
8688

8789
The above examples assume you are deploying a runtime server, but are pre-rendering some static pages in order to serve them faster and avoid hitting the server.
8890

89-
To disable runtime SSR, you can set the `ssr:false` config flag:
91+
To disable runtime SSR and configure pre-rendering to be served from a static file server, you can set the `ssr:false` config flag:
9092

9193
```ts filename=react-router.config.ts
9294
import type { Config } from "@react-router/dev/config";
9395

9496
export default {
9597
ssr: false, // disable runtime server rendering
96-
prerender: true, // pre-render static routes
98+
prerender: true, // pre-render all static routes
9799
} satisfies Config;
98100
```
99101

@@ -126,7 +128,19 @@ export default {
126128
} satisfies Config;
127129
```
128130

129-
You can configure your deployment server to serve this file for any path that otherwise would 404. Here's an example of how you can do this with the [`sirv-cli`](https://www.npmjs.com/package/sirv-cli#user-content-single-page-applications) tool:
131+
You can configure your deployment server to serve this file for any path that otherwise would 404. Some hosts do this by default, but others don't. As an example, a host may support a `_redirects` file to do this:
132+
133+
```
134+
# If you did not pre-render the `/` route
135+
/* /index.html 200
136+
137+
# If you pre-rendered the `/` route
138+
/* /__spa-fallback.html 200
139+
```
140+
141+
If you're getting 404s at valid routes for your app, it's likely you need to configure your host.
142+
143+
Here's another example of how you can do this with the [`sirv-cli`](https://www.npmjs.com/package/sirv-cli#user-content-single-page-applications) tool:
130144

131145
```sh
132146
# If you did not pre-render the `/` route

docs/how-to/spa.md

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,22 @@ There are two ways to ship a single page app with React Router
99
- **as a library** - Instead of using React Router's framework features, you can use it as a library in your own SPA architecture. Refer to [React Router as a Library](../start/library/installation) guides.
1010
- **as a framework** - This guide will focus here
1111

12-
## 1. Disable Server Rendering
12+
## Overview
13+
14+
When using React Router as a framework, you can enable "SPA Mode" by setting `ssr:false` in your `react-router.config.ts` file. This will disable runtime server rendering and generate an `index.html` at build time that you can serve and hydrate as a SPA.
15+
16+
Typical Single Page apps send a mostly blank `index.html` template with little more than an empty `<div id="root"></div>`. In contrast, `react-router build` (in SPA Mode) pre-renders your root route at build time into an `index.html` file. This means you can:
17+
18+
- Send more than an empty `<div>`
19+
- Use a root `loader` to load data for your application shell
20+
- Use React components to generate the initial page users see (root `HydrateFallback`)
21+
- Re-enable server rendering later without changing anything about your UI
22+
23+
It's important to note that setting `ssr:false` only disables _runtime server rendering_. React Router will still server render your root route at _build time_ to generate the `index.html` file. This is why your project still needs a dependency on `@react-router/node` and your routes need to be SSR-safe. That means you can't call `window` or other browser-only APIs during the initial render, even when server rendering is disabled.
24+
25+
<docs-info>SPA Mode is a special form of "Pre-Rendering" that allows you to serve all paths in your application from the same HTML file. Please refer to the [Pre-Rendering](./pre-rendering) guide if you want to do more extensive pre-rendering.</docs-info>
26+
27+
## 1. Disable Runtime Server Rendering
1328

1429
Server rendering is enabled by default. Set the `ssr` flag to `false` in `react-router.config.ts` to disable it.
1530

@@ -23,31 +38,54 @@ export default {
2338

2439
With this set to false, the server build will no longer be generated.
2540

26-
## 2. Add a `HydrateFallback` to your root route
41+
<docs-info>It's important to note that setting `ssr:false` only disables _runtime server rendering_. React Router will still server render your root route at _build time_ to generate the `index.html` file. This is why your project still needs a dependency on `@react-router/node` and your routes need to be SSR-safe. That means you can't call `window` or other browser-only APIs during the initial render, even when server rendering is disabled.</docs-info>
42+
43+
## 2. Add a `HydrateFallback` and optional `loader` to your root route
2744

2845
SPA Mode will generate an `index.html` file at build-time that you can serve as the entry point for your SPA. This will only render the root route so that it is capable of hydrating at runtime for any path in your application.
2946

30-
To provide a better/faster loading UI, you can add a `HydrateFallback` component to your root route to render your loading UI into the `index.html` at build time. This way, it will be shown to users immediately while the SPA is loading/hydrating.
47+
To provide a better loading UI than an empty `<div>`, you can add a `HydrateFallback` component to your root route to render your loading UI into the `index.html` at build time. This way, it will be shown to users immediately while the SPA is loading/hydrating.
3148

32-
```tsx filename=root.tsx
33-
import { Route } from "./+types/root";
34-
import AwesomeSpinner from "./components/spinner";
49+
```tsx filename=root.tsx lines=[7-9]
50+
import LoadingScreen from "./components/loading-screen";
3551

3652
export function Layout() {
3753
return <html>{/*...*/}</html>;
3854
}
3955

40-
// Server-rendered at build time into `index.html` (inside `<Layout>`)
4156
export function HydrateFallback() {
42-
return <AwesomeSpinner />;
57+
return <LoadingScreen />;
4358
}
4459

4560
export default function App() {
4661
return <Outlet />;
4762
}
4863
```
4964

50-
Because the root route is server-rendered at build time, you can also use a `loader` in your root route if you choose, and access the data via the optional `HydrateFallback` `loaderData` prop. You cannot in include a loader in any other routes in your app when using SPA Mode.
65+
Because the root route is server-rendered at build time, you can also use a `loader` in your root route if you choose. This `loader` will be called at build time ans the data will be available via the optional `HydrateFallback` `loaderData` prop.
66+
67+
```tsx filename=root.tsx lines=[5,10,14]
68+
import { Route } from "./+types/root";
69+
70+
export async function loader() {
71+
return {
72+
version: await getVersion(),
73+
};
74+
}
75+
76+
export function HydrateFallback({
77+
loaderData,
78+
}: Route.ComponentProps) {
79+
return (
80+
<div>
81+
<h1>Loading version {loaderData.version}...</h1>
82+
<AwesomeSpinner />
83+
</div>
84+
);
85+
}
86+
```
87+
88+
You cannot include a `loader` in any other routes in your app when using SPA Mode unless you are [pre-rendering those pages](./pre-rendering).
5189

5290
## 3. Use client loaders and client actions
5391

@@ -71,11 +109,7 @@ export async function clientAction({
71109
}
72110
```
73111

74-
## 4. Pre-rendering
75-
76-
Pre-rendering can be configured for paths with static data known at build time for faster initial page loads. Refer to [Pre-rendering](./pre-rendering) to set it up.
77-
78-
## 5. Direct all URLs to index.html
112+
## 4. Direct all URLs to index.html
79113

80114
After running `react-router build`, deploy the `build/client` directory to whatever static host you prefer.
81115

@@ -86,16 +120,3 @@ Common to deploying any SPA, you'll need to configure your host to direct all UR
86120
```
87121

88122
If you're getting 404s at valid routes for your app, it's likely you need to configure your host.
89-
90-
## Important Note
91-
92-
Typical Single Pages apps send a mostly blank `index.html` template with little more than an empty `<div id="root"></div>`.
93-
94-
In contrast `react-router build` (with server rendering disabled) pre-renders your root route at build time. This means you can:
95-
96-
- Send more than an empty div
97-
- Use a root `loader` to load data for your application shell
98-
- Use React components to generate the initial page users see (root `HydrateFallback`)
99-
- Re-enable server rendering later without changing anything about your UI
100-
101-
Therefore, setting `ssr:false` only disables _runtime server rendering_. React Router will still server render your index route at _build time_ to generate the `index.html` file. This is why your project still needs a dependency on `@react-router/node` and your routes need to be SSR-safe. That means you can't call `window` or other browser-only APIs during the initial render, even when server rendering is disabled.

0 commit comments

Comments
 (0)