UNPKG

6.57 kB Markdown View Raw
1---
2title: Data Loading
3order: 5
4---
5
6# Data Loading
7
8[MODES: framework]
9
10## Introduction
11
12Data is provided to the route component from `loader` and `clientLoader`.
13
14Loader data is automatically serialized from loaders and deserialized in components. In addition to primitive values like strings and numbers, loaders can return promises, maps, sets, dates and more.
15
16The type for the `loaderData` prop is [automatically generated][type-safety].
17
18<docs-info>We try to support the same set of [serializable types][serializable-types] that React permits server components to pass as props to client components. This future proofs your application for any eventual migration to [RSC][rsc].</docs-info>
19
20## Client Data Loading
21
22`clientLoader` is used to fetch data on the client. This is useful for pages or full projects that you'd prefer to fetch data from the browser only.
23
24```tsx filename=app/product.tsx
25// route("products/:pid", "./product.tsx");
26import type { Route } from "./+types/product";
27
28export async function clientLoader({
29 params,
30}: Route.ClientLoaderArgs) {
31 const res = await fetch(`/api/products/${params.pid}`);
32 const product = await res.json();
33 return product;
34}
35
36// HydrateFallback is rendered while the client loader is running
37export function HydrateFallback() {
38 return <div>Loading...</div>;
39}
40
41export default function Product({
42 loaderData,
43}: Route.ComponentProps) {
44 const { name, description } = loaderData;
45 return (
46 <div>
47 <h1>{name}</h1>
48 <p>{description}</p>
49 </div>
50 );
51}
52```
53
54## Server Data Loading
55
56When server rendering, `loader` is used for both initial page loads and client navigations. Client navigations call the loader through an automatic `fetch` by React Router from the browser to your server.
57
58```tsx filename=app/product.tsx
59// route("products/:pid", "./product.tsx");
60import type { Route } from "./+types/product";
61import { fakeDb } from "../db";
62
63export async function loader({ params }: Route.LoaderArgs) {
64 const product = await fakeDb.getProduct(params.pid);
65 return product;
66}
67
68export default function Product({
69 loaderData,
70}: Route.ComponentProps) {
71 const { name, description } = loaderData;
72 return (
73 <div>
74 <h1>{name}</h1>
75 <p>{description}</p>
76 </div>
77 );
78}
79```
80
81Note that the `loader` function is removed from client bundles so you can use server only APIs without worrying about them being included in the browser.
82
83### Authentication and Authorization
84
85React Router runs matched route loaders in parallel and loaders can be requested independently, such as during fetcher calls or navigations that do not revalidate every matched route. Therefore it is not possible for a parent loader to "protect" child route loaders or actions via authentication checks.
86
87Unless guarded by appropriate [server middleware][auth-middleware], each protected loader and action must authenticate the request and authorize access to the data or operation before proceeding. For example, checking that a user is signed in does not establish that they have permission to access the requested project.
88
89Middleware on a protected route or a common ancestor can enforce shared checks before the protected loaders and actions run. Any permissions not enforced by that middleware must still be checked in the individual loader or action.
90
91## Static Data Loading
92
93When pre-rendering, loaders are used to fetch data during the production build.
94
95```tsx filename=app/product.tsx
96// route("products/:pid", "./product.tsx");
97import type { Route } from "./+types/product";
98
99export async function loader({ params }: Route.LoaderArgs) {
100 let product = await getProductFromCSVFile(params.pid);
101 return product;
102}
103
104export default function Product({
105 loaderData,
106}: Route.ComponentProps) {
107 const { name, description } = loaderData;
108 return (
109 <div>
110 <h1>{name}</h1>
111 <p>{description}</p>
112 </div>
113 );
114}
115```
116
117The URLs to pre-render are specified in `react-router.config.ts`:
118
119```ts filename=react-router.config.ts
120import type { Config } from "@react-router/dev/config";
121
122export default {
123 async prerender() {
124 let products = await readProductsFromCSVFile();
125 return products.map(
126 (product) => `/products/${product.id}`,
127 );
128 },
129} satisfies Config;
130```
131
132Note that when server rendering, any URLs that aren't pre-rendered will be server rendered as usual, allowing you to pre-render some data at a single route while still server rendering the rest.
133
134## Using Both Loaders
135
136`loader` and `clientLoader` can be used together. The `loader` will be used on the server for initial SSR (or pre-rendering) and the `clientLoader` will be used on subsequent client-side navigations.
137
138```tsx filename=app/product.tsx
139// route("products/:pid", "./product.tsx");
140import type { Route } from "./+types/product";
141import { fakeDb } from "../db";
142
143export async function loader({ params }: Route.LoaderArgs) {
144 return fakeDb.getProduct(params.pid);
145}
146
147export async function clientLoader({
148 serverLoader,
149 params,
150}: Route.ClientLoaderArgs) {
151 const res = await fetch(`/api/products/${params.pid}`);
152 const serverData = await serverLoader();
153 return { ...serverData, ...(await res.json()) };
154}
155
156export default function Product({
157 loaderData,
158}: Route.ComponentProps) {
159 const { name, description } = loaderData;
160
161 return (
162 <div>
163 <h1>{name}</h1>
164 <p>{description}</p>
165 </div>
166 );
167}
168```
169
170You can also force the client loader to run during hydration and before the page renders by setting the `hydrate` property on the function. In this situation you will want to render a `HydrateFallback` component to show a fallback UI while the client loader runs.
171
172```tsx filename=app/product.tsx
173export async function loader() {
174 /* ... */
175}
176
177export async function clientLoader() {
178 /* ... */
179}
180
181// force the client loader to run during hydration
182clientLoader.hydrate = true as const; // `as const` for type inference
183
184export function HydrateFallback() {
185 return <div>Loading...</div>;
186}
187
188export default function Product() {
189 /* ... */
190}
191```
192
193---
194
195Next: [Actions][actions]
196
197See also:
198
199- [Streaming with Suspense][streaming]
200- [Client Data][client-data]
201- [Using Fetchers][fetchers]
202
203[type-safety]: ../../explanation/type-safety
204[serializable-types]: https://react.dev/reference/rsc/use-client#serializable-types
205[rsc]: ../../how-to/react-server-components
206[actions]: ./actions
207[streaming]: ../../how-to/suspense
208[client-data]: ../../how-to/client-data
209[fetchers]: ../../how-to/fetchers#loading-data
210[auth-middleware]: ../../how-to/middleware#authentication