UNPKG

10.1 kB Markdown View Raw
1---
2title: Pre-Rendering
3---
4
5# Pre-Rendering
6
7[MODES: framework]
8
9Pre-Rendering allows you to speed up page loads for static content by rendering pages at build time instead of at runtime.
10
11## Configuration
12
13Pre-rendering is enabled via the `prerender` config in `react-router.config.ts`.
14
15The simplest configuration is a boolean `true` which will pre-render all off the applications static paths based on `routes.ts`:
16
17```ts filename=react-router.config.ts
18import type { Config } from "@react-router/dev/config";
19
20export default {
21 prerender: true,
22} satisfies Config;
23```
24
25The boolean `true` will not include any dynamic paths (i.e., `/blog/:slug`) because the parameter values are unknown.
26
27To configure specific paths including dynamic values, you can specify an array of paths:
28
29```ts filename=react-router.config.ts
30import type { Config } from "@react-router/dev/config";
31
32let slugs = getPostSlugs();
33
34export default {
35 prerender: [
36 "/",
37 "/blog",
38 ...slugs.map((s) => `/blog/${s}`),
39 ],
40} satisfies Config;
41```
42
43If you need to perform more complex and/or asynchronous logic to determine the paths, you can also provide a function that returns an array of paths. This function provides you with a `getStaticPaths` method you can use to avoid manually adding all of the static paths in your application:
44
45```ts filename=react-router.config.ts
46import type { Config } from "@react-router/dev/config";
47
48export default {
49 async prerender({ getStaticPaths }) {
50 let slugs = await getPostSlugsFromCMS();
51 return [
52 ...getStaticPaths(), // "/" and "/blog"
53 ...slugs.map((s) => `/blog/${s}`),
54 ];
55 },
56} satisfies Config;
57```
58
59### Concurrency
60
61By default, pages are pre-rendered one path at a time. You can enable concurrency to pre-render multiple paths in parallel which can speed up build times in many cases. You should experiment with the value that provides the best performance for your app.
62
63To specify concurrency, move your `prerender` config down into a `prerender.paths` field and you can specify the concurrency in `prerender.concurrency`:
64
65```ts filename=react-router.config.ts
66import type { Config } from "@react-router/dev/config";
67
68let slugs = getPostSlugs();
69
70export default {
71 prerender: {
72 paths: [
73 "/",
74 "/blog",
75 ...slugs.map((s) => `/blog/${s}`),
76 ],
77 concurrency: 4,
78 },
79} satisfies Config;
80```
81
82## Pre-Rendering with/without a Runtime Server
83
84Pre-Rendering can be used with these `ssr` config values:
85
86- Alongside a runtime SSR server with `ssr:true` (the default value)
87- Deployed to a static file server with `ssr:false`
88- Alongside a runtime API server with `ssr:"unstable_api-only"` (experimental)
89
90### Pre-rendering with `ssr:true`
91
92When pre-rendering with `ssr:true`, you're indicating you will still have a runtime server but you are choosing to pre-render certain paths for quicker Response times.
93
94```ts filename=react-router.config.ts
95import type { Config } from "@react-router/dev/config";
96
97export default {
98 // Can be omitted - defaults to true
99 ssr: true,
100 prerender: ["/", "/blog", "/blog/popular-post"],
101} satisfies Config;
102```
103
104#### Data Loading and Pre-rendering
105
106There is no extra application API for pre-rendering. Routes being pre-rendered use the same route `loader` functions as server rendering:
107
108```tsx
109export async function loader({ request, params }) {
110 let post = await getPost(params.slug);
111 return post;
112}
113
114export function Post({ loaderData }) {
115 return <div>{loaderData.title}</div>;
116}
117```
118
119Instead of a request coming to your route on a deployed server, the build creates a `new Request()` and runs it through your app just like a server would.
120
121When server rendering, requests to paths that have not been pre-rendered will be server rendered as usual.
122
123#### Static File Output
124
125The rendered result will be written out to your `build/client` directory. You'll notice two files for each path:
126
127- `[url].html` HTML file for initial document requests
128- `[url].data` file for client side navigation browser requests
129
130The output of your build will indicate what files were pre-rendered:
131
132```sh
133> react-router build
134vite v5.2.11 building for production...
135...
136vite v5.2.11 building SSR bundle for production...
137...
138Prerender: Generated build/client/index.html
139Prerender: Generated build/client/blog.data
140Prerender: Generated build/client/blog/index.html
141Prerender: Generated build/client/blog/my-first-post.data
142Prerender: Generated build/client/blog/my-first-post/index.html
143...
144```
145
146During development, pre-rendering doesn't save the rendered results to the public directory, this only happens for `react-router build`.
147
148### Pre-rendering with `ssr:false`
149
150The above examples assume you are deploying a runtime server but are pre-rendering some static pages to avoid hitting the server, resulting in faster loads.
151
152To disable runtime SSR and configure pre-rendering to be served from a static file server, you can set the `ssr:false` config flag:
153
154```ts filename=react-router.config.ts
155import type { Config } from "@react-router/dev/config";
156
157export default {
158 ssr: false, // disable runtime server rendering
159 prerender: true, // pre-render all static routes
160} satisfies Config;
161```
162
163If you specify `ssr:false` without a `prerender` config, React Router refers to that as [SPA Mode](./spa). In SPA Mode, we render a single HTML file that is capable of hydrating for _any_ of your application paths. It can do this because it only renders the `root` route into the HTML file and then determines which child routes to load based on the browser URL during hydration. This means you can use a `loader` on the root route, but not on any other routes because we don't know which routes to load until hydration in the browser.
164
165If you want to pre-render paths with `ssr:false`, those matched routes _can_ have loaders because we'll pre-render all of the matched routes for those paths, not just the root. You cannot include `actions` or `headers` functions in any routes when `ssr:false` is set because there will be no runtime server to run them on.
166
167#### Pre-rendering with a SPA Fallback
168
169If you want `ssr:false` but don't want to pre-render _all_ of your routes - that's fine too! You may have some paths where you need the performance/SEO benefits of pre-rendering, but other pages where a SPA would be fine.
170
171You can do this using the combination of config options as well - just limit your `prerender` config to the paths that you want to pre-render and React Router will also output a "SPA Fallback" HTML file that can be served to hydrate any other paths (using the same approach as [SPA Mode](./spa)).
172
173This will be written to one of the following paths:
174
175- `build/client/index.html` - If the `/` path is not pre-rendered
176- `build/client/__spa-fallback.html` - If the `/` path is pre-rendered
177
178```ts filename=react-router.config.ts
179import type { Config } from "@react-router/dev/config";
180
181export default {
182 ssr: false,
183
184 // SPA fallback will be written to build/client/index.html
185 prerender: ["/about-us"],
186
187 // SPA fallback will be written to build/client/__spa-fallback.html
188 prerender: ["/", "/about-us"],
189} satisfies Config;
190```
191
192You 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:
193
194```
195# If you did not pre-render the `/` route
196/* /index.html 200
197
198# If you pre-rendered the `/` route
199/* /__spa-fallback.html 200
200```
201
202If you're getting 404s at valid routes for your app, it's likely you need to configure your host.
203
204Here'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:
205
206```sh
207# If you did not pre-render the `/` route
208sirv-cli build/client --single index.html
209
210# If you pre-rendered the `/` route
211sirv-cli build/client --single __spa-fallback.html
212```
213
214#### Invalid Exports
215
216When pre-rendering with `ssr:false`, React Router will error at build time if you have invalid exports to help prevent some mistakes that can be easily overlooked.
217
218- `headers`/`action` functions are prohibited in all routes because there will be no runtime server on which to run them
219- When using `ssr:false` without a `prerender` config (SPA Mode), a `loader` is permitted on the root route only
220- When using `ssr:false` with a `prerender` config, a `loader` is permitted on any route matched by a `prerender` path
221 - If you are using a `loader` on a pre-rendered route that has child routes, you will need to make sure the parent `loaderData` can be determined at run-time properly by either:
222 - Pre-rendering all child routes so that the parent `loader` can be called at build-time for each child route path and rendered into a `.data` file, or
223 - Use a `clientLoader` on the parent that can be called at run-time for non-pre-rendered child paths
224
225### Pre-rendering with API-only Mode (unstable)
226
227<docs-warning>API-only Mode is experimental and may change in minor or patch releases. It is not supported in RSC Framework Mode.</docs-warning>
228
229Use `ssr: "unstable_api-only"` with `prerender` to generate documents at build time while keeping a runtime server for loaders, actions, and route discovery:
230
231```ts filename=react-router.config.ts
232import type { Config } from "@react-router/dev/config";
233
234export default {
235 ssr: "unstable_api-only",
236 prerender: ["/"],
237} satisfies Config;
238```
239
240Document generation follows the same behavior as `ssr:false`: selected paths produce HTML and `.data` files, and other paths use a SPA fallback. The fallback is `__spa-fallback.html` when `/` is prerendered, or `index.html` otherwise.
241
242Prerendered pages hydrate using their build-time data. Pages using the fallback fetch server loader data during initialization. Server loaders and actions are available on all routes, including those outside the prerender list, and normal loader revalidation remains enabled.
243
244See [API-only Mode](../start/framework/rendering#api-only-mode-unstable) for deployment routing and configuring a separate API origin.
245
\No newline at end of file