UNPKG

5.61 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## Static Data Loading
84
85When pre-rendering, loaders are used to fetch data during the production build.
86
87```tsx filename=app/product.tsx
88// route("products/:pid", "./product.tsx");
89import type { Route } from "./+types/product";
90
91export async function loader({ params }: Route.LoaderArgs) {
92 let product = await getProductFromCSVFile(params.pid);
93 return product;
94}
95
96export default function Product({
97 loaderData,
98}: Route.ComponentProps) {
99 const { name, description } = loaderData;
100 return (
101 <div>
102 <h1>{name}</h1>
103 <p>{description}</p>
104 </div>
105 );
106}
107```
108
109The URLs to pre-render are specified in `react-router.config.ts`:
110
111```ts filename=react-router.config.ts
112import type { Config } from "@react-router/dev/config";
113
114export default {
115 async prerender() {
116 let products = await readProductsFromCSVFile();
117 return products.map(
118 (product) => `/products/${product.id}`,
119 );
120 },
121} satisfies Config;
122```
123
124Note 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.
125
126## Using Both Loaders
127
128`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.
129
130```tsx filename=app/product.tsx
131// route("products/:pid", "./product.tsx");
132import type { Route } from "./+types/product";
133import { fakeDb } from "../db";
134
135export async function loader({ params }: Route.LoaderArgs) {
136 return fakeDb.getProduct(params.pid);
137}
138
139export async function clientLoader({
140 serverLoader,
141 params,
142}: Route.ClientLoaderArgs) {
143 const res = await fetch(`/api/products/${params.pid}`);
144 const serverData = await serverLoader();
145 return { ...serverData, ...(await res.json()) };
146}
147
148export default function Product({
149 loaderData,
150}: Route.ComponentProps) {
151 const { name, description } = loaderData;
152
153 return (
154 <div>
155 <h1>{name}</h1>
156 <p>{description}</p>
157 </div>
158 );
159}
160```
161
162You 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.
163
164```tsx filename=app/product.tsx
165export async function loader() {
166 /* ... */
167}
168
169export async function clientLoader() {
170 /* ... */
171}
172
173// force the client loader to run during hydration
174clientLoader.hydrate = true as const; // `as const` for type inference
175
176export function HydrateFallback() {
177 return <div>Loading...</div>;
178}
179
180export default function Product() {
181 /* ... */
182}
183```
184
185---
186
187Next: [Actions][actions]
188
189See also:
190
191- [Streaming with Suspense][streaming]
192- [Client Data][client-data]
193- [Using Fetchers][fetchers]
194
195[type-safety]: ../../explanation/type-safety
196[serializable-types]: https://react.dev/reference/rsc/use-client#serializable-types
197[rsc]: ../../how-to/react-server-components
198[actions]: ./actions
199[streaming]: ../../how-to/suspense
200[client-data]: ../../how-to/client-data
201[fetchers]: ../../how-to/fetchers#loading-data