INDIE UNPKG

5.06 kBMarkdownView Raw
1---
2title: Single Page App (SPA)
3---
4
5# Single Page App (SPA)
6
7[MODES: framework]
8
9<br/>
10<br/>
11
12<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>
13
14When 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
16Typical 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<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>
24
25<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>
26
27## 1. Disable Runtime Server Rendering
28
29Server rendering is enabled by default. Set the `ssr` flag to `false` in `react-router.config.ts` to disable it.
30
31```ts filename=react-router.config.ts lines=[4]
32import { type Config } from "@react-router/dev/config";
33
34export default {
35 ssr: false,
36} satisfies Config;
37```
38
39With this set to false, the server build will no longer be generated.
40
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
44
45SPA 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.
46
47To 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.
48
49```tsx filename=root.tsx lines=[7-9]
50import LoadingScreen from "./components/loading-screen";
51
52export function Layout() {
53 return <html>{/*...*/}</html>;
54}
55
56export function HydrateFallback() {
57 return <LoadingScreen />;
58}
59
60export default function App() {
61 return <Outlet />;
62}
63```
64
65Because 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.
66
67```tsx filename=root.tsx lines=[5,10,14]
68import { Route } from "./+types/root";
69
70export async function loader() {
71 return {
72 version: await getVersion(),
73 };
74}
75
76export 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
88You cannot include a `loader` in any other routes in your app when using SPA Mode unless you are [pre-rendering those pages](./pre-rendering).
89
90## 3. Use client loaders and client actions
91
92With server rendering disabled, you can still use `clientLoader` and `clientAction` to manage route data and mutations.
93
94```tsx filename=some-route.tsx
95import { Route } from "./+types/some-route";
96
97export async function clientLoader({
98 params,
99}: Route.ClientLoaderArgs) {
100 let data = await fetch(`/some/api/stuff/${params.id}`);
101 return data;
102}
103
104export async function clientAction({
105 request,
106}: Route.ClientActionArgs) {
107 let formData = await request.formData();
108 return await processPayment(formData);
109}
110```
111
112## 4. Direct all URLs to index.html
113
114After running `react-router build`, deploy the `build/client` directory to whatever static host you prefer.
115
116Common 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:
117
118```
119/* /index.html 200
120```
121
122If you're getting 404s at valid routes for your app, it's likely you need to configure your host.