UNPKG

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