This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
Data loaders
Edit page
Learn how to fetch data on the server using data loaders in Expo Router.
Data loaders are stable in SDK 58 and later. For SDK 55–57, enable
unstable_useServerDataLoadersin theexpo-routerconfig plugin. They require either static rendering or server rendering.
Data loaders enable server-side data fetching for your routes. By exporting a loader function from a route file, you can fetch data on the server and access it in your component using the useLoaderData hook. This lets you keep sensitive data and API keys on the server while providing your components with the data they need.
Setup
1
Configure your web output mode. Data loaders work with both static rendering (web.output: 'static') and server rendering (web.output: 'server'):
2
Start the development server:
Expo Skills for AI agents
If you use an AI agent, install Expo Skills to teach it data fetching patterns, including data loaders:
Use when implementing or debugging ANY network request, API call, or data fetching.
Basic example
Export a loader function from your route file and use the useLoaderData hook to access the data in your component:
The loader function executes on the server, and its return value is serialized and passed to your component. This means you can safely use server-side secrets, database connections, and other resources that should not be exposed to the client. When using TypeScript, passing typeof loader as the generic parameter to useLoaderData allows the hook to infer the return type from your loader function.
The
useLoaderDatahook does not need to be called in the route component itself. It can be called in any child component within the route's component tree.
Using Suspense
When a component calls the useLoaderData hook while data is still loading, React suspends that component. The loading state cascades up the component tree until it reaches the nearest <Suspense> boundary, which then renders its fallback.
This lets you control exactly where loading fallbacks appear by placing <Suspense> boundaries in your component tree:
In the above example, useLoaderData is in a child component of <Home> and wrapped it with <Suspense> to show a loading state.
Error handling
When a loader throws an error, it propagates to the nearest error boundary. You can export an ErrorBoundary component from the same route file to handle loader errors:
When no ErrorBoundary is exported, the error propagates to the nearest parent route's error boundary. You can also use custom error boundary components within your route to catch errors at specific points in the component tree.
Dynamic routes
Loaders receive route parameters as the second argument:
Accessing the request
The
requestparameter isundefinedwhen using static rendering because there is no HTTP request at build-time.
When using server rendering, loaders receive the incoming HTTP request as the first argument. This allows you to access headers, cookies, and other request information:
Returning data
Loaders can return data as plain JSON, which is easily deserialized using JSON.parse. This includes objects, arrays or any other primitive that can be serialized with JSON.stringify.
If your loader returns undefined or null, the value is normalized to null.
In SDK 58 and later, you can also return a Response with a JSON body to set response headers. The useLoaderData hook reads the JSON data from the response:
Use public only for data that can be shared between users. For personalized data, use private or no-store as appropriate. See Caching and revalidation for how these headers affect navigation and static exports.
Runtime API
Data loaders have full access to the Runtime API from expo-server. This includes utilities for setting response headers, throwing HTTP errors, and running background tasks:
See the Runtime API documentation for a full list of available functions.
Caching and revalidation
This section describes SDK 58 and later. In SDK 55–57, Expo Router caches loader data on the client during navigation.
Expo Router uses loader data from the initial HTML response for hydration. When you revisit a route after it unmounts, useLoaderData fetches its data again. The platform HTTP cache uses Cache-Control to decide whether to reuse a cached response, revalidate it, or request fresh data. Expo Router does not keep a permanent in-memory data cache across route visits.
A revisit can display a Suspense fallback while the fetch completes, even when the platform serves a cached response. Retrying a loader error through the route's ErrorBoundary also fetches again.
Cache defaults
When a loader does not declare Cache-Control, these defaults apply:
If the loader returns a Cache-Control header, it takes precedence over these defaults.
Static exports
In a static export, a loader's Cache-Control header applies to both the data file and the HTML page that embeds the data. Static exports keep only the Cache-Control header from loader responses.
The expo-server package, EAS Hosting, and npx expo serve apply these rules when serving your export. Other static hosts need their own header configuration. HTTP caching does not rerun a static loader. Its data changes only when you export and deploy the app again.
Environment variables
Loaders run on the server and have access to process.env. Environment variables used in loaders are never exposed to the client bundle. This is useful for accessing API keys and other secrets:
Difference between static and server rendering
Data loaders behave differently depending on your web.output configuration:
Static rendering
With static rendering, loaders execute during when exporting your app with npx expo export. The data is embedded in the generated HTML and JSON files. This means:
- Data is determined at build-time and does not change until the next build
- The
requestparameter isundefinedbecause there is no HTTP request during the build - Ideal for content that does not change frequently
Server rendering
With server rendering, loaders execute on every request. This means:
- The
requestparameter contains an immutable version of the incoming HTTP request - Requires
expo-serverfor production deployment
Typed loader functions
expo-server provides two helper functions that create loaders with improved type safety. They narrow the callback signature so you only receive the parameters relevant to your rendering mode.
createStaticLoader
Use createStaticLoader for routes that only need route parameters. The callback only receives the route params, and is safe to use with both static and server rendering:
createServerLoader
Use createServerLoader for routes that need access to the incoming HTTP request. The callback receives an ImmutableRequest and the route params as arguments:
createServerLoaderwill throw an error if called during static site generation (SSG) because there is no HTTP request at build time. UsecreateStaticLoaderwhen using static rendering.
Using LoaderFunction directly
You can also type loaders directly using the LoaderFunction type from expo-router/server. This gives you full control over the function signature, including both request and params:
Known limitations
- Loaders must return JSON-serializable data or a
Responsewith a JSON body. Streaming loader data is not supported. - There is no public API to invalidate loader data for a route that remains mounted. Use
Cache-Controlto control HTTP caching when the route fetches again.