Skip to main content
This page walks through how to fetch data in Server and Client Components, how to stream components that depend on slow data, and patterns for sequential and parallel data fetching.

Server Components

You can fetch data in Server Components using any asynchronous I/O.

With the fetch API

Turn your component into an async function and await the fetch call:
Identical fetch requests in a React component tree are memoized by default, so you can fetch data in the component that needs it without worrying about duplicate network requests. Fetch requests are not cached by default and will block the page from rendering. Use the use cache directive to cache results, or wrap the fetching component in <Suspense> to stream fresh data at request time.

With an ORM or database

Since Server Components run on the server, credentials and query logic will never reach the client bundle. You can safely make database queries directly:
Always ensure requests are properly authenticated and authorized. See the data security guide for best practices on securing server-side data access.

Streaming

When data is slow, the whole route is blocked until all fetches complete. You can break the page into smaller chunks and progressively stream them to the client using React Suspense. There are two ways to add streaming:

With loading.js

Create a loading.js file in the same folder as your page to stream the entire page while data is being fetched:
On navigation, the user immediately sees the layout and the loading state while the page renders. The new content is swapped in automatically once rendering completes. Behind the scenes, loading.js is nested inside layout.js and automatically wraps the page.js file in a <Suspense> boundary.
A layout that accesses uncached or runtime data (e.g. cookies(), headers(), or uncached fetches) does not fall back to a same-segment loading.js. Instead, it blocks navigation until the layout finishes rendering. Wrap uncached access in its own <Suspense> boundary, or move the data fetching into page.js where loading.js covers it.

With <Suspense>

<Suspense> gives you more granular control. You can immediately show page content that falls outside the boundary while streaming content inside it:
Design loading states to be meaningful. For example, use skeletons or spinners, or show a meaningful part of the future screen such as a cover photo and title. In development, you can preview and inspect loading states using the React DevTools.

Client Components

There are two ways to fetch data in Client Components.

Streaming data with the use API

Fetch data in a Server Component, pass the unawaited promise to your Client Component as a prop, and resolve it there with React’s use API:
In the Client Component, use the use API to read the promise:

Community libraries

You can use community libraries like SWR or React Query to fetch data in Client Components. For example, with SWR:

Examples

Sequential data fetching

Sequential fetching happens when one request depends on data from another. Use <Suspense> to stream in dependent data without blocking the entire page:

Parallel data fetching

Parallel fetching starts multiple requests at the same time. Use Promise.all to initiate requests in parallel and avoid waterfalls:
If one request fails when using Promise.all, the entire operation fails. Use Promise.allSettled to handle partial failures instead.

Sharing data with context and React.cache

You can share fetched data across both Server and Client Components by combining React.cache with context providers. Create a cached data function:
Create a context provider that stores the promise:
In a layout, pass the promise to the provider without awaiting it:
Client Components resolve the promise using use(), wrapped in <Suspense> for a fallback:
Server Components can also call getUser() directly — because it’s wrapped with React.cache, multiple calls within the same request return the same memoized result:
React.cache is scoped to the current request only. Each request gets its own memoization scope — results are never shared between requests.