UNPKG

5.04 kB Markdown View Raw
1---
2title: Single Page App (SPA)
3---
4
5# Single Page App (SPA)
6
7[MODES: framework]
8
9<docs-info>This guide focuses on how to build Single Page Apps with React Router Framework mode. If you're using React Router in declarative or data mode, you can design your own SPA architecture.</docs-info>
10
11When 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.
12
13Typical 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:
14
15- Send more than an empty `<div>`
16- Use a root `loader` to load data for your application shell
17- Use React components to generate the initial page users see (root `HydrateFallback`)
18- Re-enable server rendering later without changing anything about your UI
19
20<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>
21
22<docs-info>If you want a SPA frontend backed by React Router server loaders and actions, see experimental [API-only Mode](../start/framework/rendering#api-only-mode-unstable).</docs-info>
23
24## 1. Disable Runtime Server Rendering
25
26Server rendering is enabled by default. Set the `ssr` flag to `false` in `react-router.config.ts` to disable it.
27
28```ts filename=react-router.config.ts lines=[4]
29import { type Config } from "@react-router/dev/config";
30
31export default {
32 ssr: false,
33} satisfies Config;
34```
35
36With this set to false, the server build will no longer be generated.
37
38<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>
39
40## 2. Add a `HydrateFallback` and optional `loader` to your root route
41
42SPA 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.
43
44To 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.
45
46```tsx filename=root.tsx lines=[7-9]
47import LoadingScreen from "./components/loading-screen";
48
49export function Layout() {
50 return <html>{/*...*/}</html>;
51}
52
53export function HydrateFallback() {
54 return <LoadingScreen />;
55}
56
57export default function App() {
58 return <Outlet />;
59}
60```
61
62Because 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 and the data will be available via the optional `HydrateFallback` `loaderData` prop.
63
64```tsx filename=root.tsx lines=[5,10,14]
65import { Route } from "./+types/root";
66
67export async function loader() {
68 return {
69 version: await getVersion(),
70 };
71}
72
73export function HydrateFallback({
74 loaderData,
75}: Route.ComponentProps) {
76 return (
77 <div>
78 <h1>Loading version {loaderData.version}...</h1>
79 <AwesomeSpinner />
80 </div>
81 );
82}
83```
84
85You cannot include a `loader` in any other routes in your app when using SPA Mode unless you are [pre-rendering those pages](./pre-rendering).
86
87## 3. Use client loaders and client actions
88
89With server rendering disabled, you can still use `clientLoader` and `clientAction` to manage route data and mutations.
90
91```tsx filename=some-route.tsx
92import { Route } from "./+types/some-route";
93
94export async function clientLoader({
95 params,
96}: Route.ClientLoaderArgs) {
97 let data = await fetch(`/some/api/stuff/${params.id}`);
98 return data;
99}
100
101export async function clientAction({
102 request,
103}: Route.ClientActionArgs) {
104 let formData = await request.formData();
105 return await processPayment(formData);
106}
107```
108
109## 4. Direct all URLs to index.html
110
111After running `react-router build`, deploy the `build/client` directory to whatever static host you prefer.
112
113Common to deploying any SPA, you'll need to configure your host to direct all URLs to the `index.html` of the client build. Some hosts do this by default, but others don't. As an example, a host may support a `_redirects` file to do this:
114
115```
116/* /index.html 200
117```
118
119If you're getting 404s at valid routes for your app, it's likely you need to configure your host.