Data Fetching
Good to know:
- This new data fetching model is currently being developed by the React team. We recommend reading the support for promises React RFC which introduces
async
andawait
in Server Components and a newuse()
hook for Client Components.- While you can try it out, it is not yet stable. We'll keep these docs updated to reflect the latest developments.
React and Next.js 13 introduced a new way to fetch and manage data in your application. The new data fetching system works in the app
directory and is built on top of the fetch()
Web API.
fetch()
is a Web API used to fetch remote resources that returns a promise. React extends fetch
to provide automatic request deduping, and Next.js extends the fetch
options object to allow each request to set its own caching and revalidating.
async
and await
in Server Components
With the proposed React RFC, you can use async
and await
to fetch data in Server Components.
async function getData() {
const res = await fetch('https://api.example.com/...');
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
// Recommendation: handle errors
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <main></main>;
}
Async Server Component TypeScript Error
- An
async
Server Components will cause a'Promise<Element>' is not a valid JSX element
type error where it is used.- This is a known issue with TypeScript and is being worked on upstream.
- As a temporary workaround, you can add
{/* @ts-expect-error Async Server Component */}
above the component to disable type checking for it.
Server Component Functions
Next.js provides helpful server functions you may need when fetching data in Server Components:
use
in Client Components
use
is a new React function that accepts a promise conceptually similar to await
. use
handles the promise returned by a function in a way that is compatible with components, hooks, and Suspense. Learn more about use
in the React RFC.
Wrapping fetch
in use
is currently not recommended in Client Components and may trigger multiple re-renders. For now, if you need to fetch data in a Client Component, we recommend using a third-party library such as SWR or React Query.
Note: We'll be adding more examples once
fetch
anduse
work in Client Components.
Static Data Fetching
By default, fetch
will automatically fetch and cache data indefinitely.
fetch('https://...'); // cache: 'force-cache' is the default
Revalidating Data
To revalidate cached data at a timed interval, you can use the next.revalidate
option in fetch()
to set the cache
lifetime of a resource (in seconds).
fetch('https://...', { next: { revalidate: 10 } });
See Revalidating Data for more information.
NOTE: Caching at the fetch level via
revalidate
orcache: 'force-cache'
stores the data across requests in a shared cache. You should avoid using it for user-specific data (i.e. requests that derive data fromcookies()
orheaders()
)
Dynamic Data Fetching
To fetch fresh data on every fetch
request, use the cache: 'no-store'
option.
fetch('https://...', { cache: 'no-store' });
Data Fetching Patterns
Parallel Data Fetching
To minimize client-server waterfalls, we recommend this pattern to fetch data in parallel:
import Albums from './albums';
async function getArtist(username: string) {
const res = await fetch(`https://api.example.com/artist/${username}`);
return res.json();
}
async function getArtistAlbums(username: string) {
const res = await fetch(`https://api.example.com/artist/${username}/albums`);
return res.json();
}
export default async function Page({
params: { username },
}: {
params: { username: string };
}) {
// Initiate both requests in parallel
const artistData = getArtist(username);
const albumsData = getArtistAlbums(username);
// Wait for the promises to resolve
const [artist, albums] = await Promise.all([artistData, albumsData]);
return (
<>
<h1>{artist.name}</h1>
<Albums list={albums}></Albums>
</>
);
}
By starting the fetch prior to calling await
in the Server Component, each request can eagerly start to fetch requests at the same time. This sets the components up so you can avoid waterfalls.
We can save time by initiating both requests in parallel, however, the user won't see the rendered result until both promises are resolved.
To improve the user experience, you can add a suspense boundary to break up the rendering work and show part of the result as soon as possible:
import { getArtist, getArtistAlbums, type Album } from './api';
export default async function Page({
params: { username },
}: {
params: { username: string };
}) {
// Initiate both requests in parallel
const artistData = getArtist(username);
const albumData = getArtistAlbums(username);
// Wait for the artist's promise to resolve first
const artist = await artistData;
return (
<>
<h1>{artist.name}</h1>
{/* Send the artist information first,
and wrap albums in a suspense boundary */}
<Suspense fallback={<div>Loading...</div>}>
{/* @ts-expect-error Async Server Component */}
<Albums promise={albumData} />
</Suspense>
</>
);
}
// Albums Component
async function Albums({ promise }: { promise: Promise<Album[]> }) {
// Wait for the albums promise to resolve
const albums = await promise;
return (
<ul>
{albums.map((album) => (
<li key={album.id}>{album.name}</li>
))}
</ul>
);
}
Take a look at the preloading pattern for more information on improving components structure.
Sequential Data Fetching
To fetch data sequentially, you can fetch
directly inside the component that needs it, or you can await
the result of fetch
inside the component that needs it:
// ...
async function Playlists({ artistID }: { artistID: string }) {
// Wait for the playlists
const playlists = await getArtistPlaylists(artistID);
return (
<ul>
{playlists.map((playlist) => (
<li key={playlist.id}>{playlist.name}</li>
))}
</ul>
);
}
export default async function Page({
params: { username },
}: {
params: { username: string };
}) {
// Wait for the artist
const artist = await getArtist(username);
return (
<>
<h1>{artist.name}</h1>
<Suspense fallback={<div>Loading...</div>}>
{/* @ts-expect-error Async Server Component */}
<Playlists artistID={artist.id} />
</Suspense>
</>
);
}
By fetching data inside the component, each fetch request and nested segment in the route cannot start fetching data and rendering until the previous request or segment has completed.
Blocking Rendering in a Route
By fetching data in a layout, rendering for all route segments beneath it can only start once the data has finished loading.
In the pages
directory, pages using server-rendering would show the browser loading spinner until getServerSideProps
had finished, then render the React component for that page. This can be described as "all or nothing" data fetching. Either you had the entire data for your page, or none.
In the app
directory, you have additional options to explore:
- First, you can use
loading.js
to show an instant loading state from the server while streaming in the result from your data fetching function. - Second, you can move data fetching lower in the component tree to only block rendering for the parts of the page that need it. For example, moving data fetching to a specific component rather than fetching it at the root layout.
Whenever possible, it's best to fetch data in the segment that uses it. This also allows you to show a loading state for only the part of the page that is loading, and not the entire page.
Data Fetching without fetch()
You might not always have the ability to use and configure fetch
requests directly if you're using a third-party library such as an ORM or database client.
In cases where you cannot use fetch
but still want to control the caching or revalidating behavior of a layout or page, you can rely on the default caching behavior of the segment or use the segment cache configuration.
Default Caching Behavior
Any data fetching libraries that do not use fetch
directly will not affect caching of a route, and will be static or dynamic depending on the route segment.
If the segment is static (default), the output of the request will be cached and revalidated (if configured) alongside the rest of the segment. If the segment is dynamic, the output of the request will not be cached and will be re-fetched on every request when the segment is rendered.
Good to know: Dynamic functions like
cookies()
andheaders()
will make the route segment dynamic.
Segment Cache Configuration
As a temporary solution, until the caching behavior of third-party queries can be configured, you can use segment configuration to customize the cache behavior of the entire segment.
import prisma from './lib/prisma';
export const revalidate = 3600; // revalidate every hour
async function getPosts() {
const posts = await prisma.post.findMany();
return posts;
}
export default async function Page() {
const posts = await getPosts();
// ...
}