Movatterモバイル変換


[0]ホーム

URL:


Skip to content
Important
Security Advisory: React2Shell & two new vulnerabilities
Find out more

Fetching Data

Last updated November 5, 2025

This page will walk you through how you can fetch data inServer and Client Components, and how tostream components that depend on data.

Fetching data

Server Components

You can fetch data in Server Components using any asynchronous I/O, such as:

  1. Thefetch API
  2. AnORM or database
  3. Reading from the filesystem using Node.js APIs likefs

With thefetch API

To fetch data with thefetch API, turn your component into an asynchronous function, and await thefetch call. For example:

app/blog/page.tsx
exportdefaultasyncfunctionPage() {constdata=awaitfetch('https://api.vercel.app/blog')constposts=awaitdata.json()return (    <ul>      {posts.map((post)=> (        <likey={post.id}>{post.title}</li>      ))}    </ul>  )}

Good to know:

  • fetch responses are not cached by default. However, Next.js willpre-render the route and the output will be cached for improved performance. If you'd like to opt intodynamic rendering, use the{ cache: 'no-store' } option. See thefetch API Reference.
  • During development, you can logfetch calls for better visibility and debugging. See thelogging API reference.

With an ORM or database

Since Server Components are rendered on the server, you can safely make database queries using an ORM or database client. Turn your component into an asynchronous function, and await the call:

app/blog/page.tsx
import { db, posts }from'@/lib/db'exportdefaultasyncfunctionPage() {constallPosts=awaitdb.select().from(posts)return (    <ul>      {allPosts.map((post)=> (        <likey={post.id}>{post.title}</li>      ))}    </ul>  )}

Client Components

There are two ways to fetch data in Client Components, using:

  1. React'suse hook
  2. A community library likeSWR orReact Query

Streaming data with theuse hook

You can use React'suse hook tostream data from the server to client. Start by fetching data in your Server component, and pass the promise to your Client Component as prop:

app/blog/page.tsx
import Postsfrom'@/app/ui/posts'import { Suspense }from'react'exportdefaultfunctionPage() {// Don't await the data fetching functionconstposts=getPosts()return (    <Suspensefallback={<div>Loading...</div>}>      <Postsposts={posts} />    </Suspense>  )}

Then, in your Client Component, use theuse hook to read the promise:

app/ui/posts.tsx
'use client'import { use }from'react'exportdefaultfunctionPosts({  posts,}: {  posts:Promise<{ id:string; title:string }[]>}) {constallPosts=use(posts)return (    <ul>      {allPosts.map((post)=> (        <likey={post.id}>{post.title}</li>      ))}    </ul>  )}

In the example above, the<Posts> component is wrapped in a<Suspense> boundary. This means the fallback will be shown while the promise is being resolved. Learn more aboutstreaming.

Community libraries

You can use a community library likeSWR orReact Query to fetch data in Client Components. These libraries have their own semantics for caching, streaming, and other features. For example, with SWR:

app/blog/page.tsx
'use client'import useSWRfrom'swr'constfetcher= (url)=>fetch(url).then((r)=>r.json())exportdefaultfunctionBlogPage() {const {data,error,isLoading }=useSWR('https://api.vercel.app/blog',    fetcher  )if (isLoading)return <div>Loading...</div>if (error)return <div>Error: {error.message}</div>return (    <ul>      {data.map((post: { id:string; title:string })=> (        <likey={post.id}>{post.title}</li>      ))}    </ul>  )}

Deduplicate requests and cache data

One way to deduplicatefetch requests is withrequest memoization. With this mechanism,fetch calls usingGET orHEAD with the same URL and options in a single render pass are combined into one request. This happens automatically, and you canopt out by passing an Abort signal tofetch.

Request memoization is scoped to the lifetime of a request.

You can also deduplicatefetch requests by using Next.js’Data Cache, for example by settingcache: 'force-cache' in yourfetch options.

Data Cache allows sharing data across the current render pass and incoming requests.

If you arenot usingfetch, and instead using an ORM or database directly, you can wrap your data access with theReactcache function.

app/lib/data.ts
import { cache }from'react'import { db, posts, eq }from'@/lib/db'exportconstgetPost=cache(async (id:string)=> {constpost=awaitdb.query.posts.findFirst({    where:eq(posts.id,parseInt(id)),  })})

Streaming

Warning: The content below assumes thecacheComponents config option is enabled in your application. The flag was introduced in Next.js 15 canary.

When you fetch data in Server Components, the data is fetched and rendered on the server for each request. If you have any slow data requests, the whole route will be blocked from rendering until all the data is fetched.

To improve the initial load time and user experience, you can use streaming to break up the page's HTML into smaller chunks and progressively send those chunks from the server to the client.

How Server Rendering with Streaming WorksHow Server Rendering with Streaming Works

There are two ways you can leverage streaming in your application:

  1. Wrapping a page with aloading.js file
  2. Wrapping a component with<Suspense>

Withloading.js

You can create aloading.js file in the same folder as your page to stream theentire page while the data is being fetched. For example, to streamapp/blog/page.js, add the file inside theapp/blog folder.

Blog folder structure with loading.js fileBlog folder structure with loading.js file
app/blog/loading.tsx
exportdefaultfunctionLoading() {// Define the Loading UI herereturn <div>Loading...</div>}

On navigation, the user will immediately see the layout and aloading state while the page is being rendered. The new content will then be automatically swapped in once rendering is complete.

Loading UILoading UI

Behind-the-scenes,loading.js will be nested insidelayout.js, and will automatically wrap thepage.js file and any children below in a<Suspense> boundary.

loading.js overviewloading.js overview

This approach works well for route segments (layouts and pages), but for more granular streaming, you can use<Suspense>.

With<Suspense>

<Suspense> allows you to be more granular about what parts of the page to stream. For example, you can immediately show any page content that falls outside of the<Suspense> boundary, and stream in the list of blog posts inside the boundary.

app/blog/page.tsx
import { Suspense }from'react'import BlogListfrom'@/components/BlogList'import BlogListSkeletonfrom'@/components/BlogListSkeleton'exportdefaultfunctionBlogPage() {return (    <div>      {/* This content will be sent to the client immediately */}      <header>        <h1>Welcome to the Blog</h1>        <p>Read the latest posts below.</p>      </header>      <main>        {/* If there's any dynamic content inside this boundary, it will be streamed in */}        <Suspensefallback={<BlogListSkeleton />}>          <BlogList />        </Suspense>      </main>    </div>  )}

Creating meaningful loading states

An instant loading state is fallback UI that is shown immediately to the user after navigation. For the best user experience, we recommend designing loading states that are meaningful and help users understand the app is responding. For example, you can use skeletons and spinners, or a small but meaningful part of future screens such as a cover photo, title, etc.

In development, you can preview and inspect the loading state of your components using theReact Devtools.

Examples

Sequential data fetching

Sequential data fetching happens when one request depends on data from another.

For example,<Playlists> can only fetch data after<Artist> completes because it needs theartistID:

app/artist/[username]/page.tsx
exportdefaultasyncfunctionPage({  params,}: {  params:Promise<{ username:string }>}) {const {username }=await params// Getartist informationconstartist=awaitgetArtist(username)return (    <>      <h1>{artist.name}</h1>      {/* Show fallback UI while the Playlists component is loading */}      <Suspensefallback={<div>Loading...</div>}>        {/* Pass theartist ID to the Playlists component */}        <PlaylistsartistID={artist.id} />      </Suspense>    </>  )}asyncfunctionPlaylists({artistID }: {artistID:string }) {// Use theartist ID to fetch playlistsconstplaylists=awaitgetArtistPlaylists(artistID)return (    <ul>      {playlists.map((playlist)=> (        <likey={playlist.id}>{playlist.name}</li>      ))}    </ul>  )}

In this example,<Suspense> allows the playlists to stream in after the artist data loads. However, the page still waits for the artist data before displaying anything. To prevent this, you can wrap the entire page component in a<Suspense> boundary (for example, using aloading.js file) to show a loading state immediately.

Ensure your data source can resolve the first request quickly, as it blocks everything else. If you can't optimize the request further, considercaching the result if the data changes infrequently.

Parallel data fetching

Parallel data fetching happens when data requests in a route are eagerly initiated and start at the same time.

By default,layouts and pages are rendered in parallel. So each segment starts fetching data as soon as possible.

However, withinany component, multipleasync/await requests can still be sequential if placed after the other. For example,getAlbums will be blocked untilgetArtist is resolved:

app/artist/[username]/page.tsx
import { getArtist, getAlbums }from'@/app/lib/data'exportdefaultasyncfunctionPage({ params }) {// These requests will be sequentialconst {username }=await paramsconstartist=awaitgetArtist(username)constalbums=awaitgetAlbums(username)return <div>{artist.name}</div>}

Start multiple requests by callingfetch, then await them withPromise.all. Requests begin as soon asfetch is called.

app/artist/[username]/page.tsx
import Albumsfrom'./albums'asyncfunctiongetArtist(username:string) {constres=awaitfetch(`https://api.example.com/artist/${username}`)returnres.json()}asyncfunctiongetAlbums(username:string) {constres=awaitfetch(`https://api.example.com/artist/${username}/albums`)returnres.json()}exportdefaultasyncfunctionPage({  params,}: {  params:Promise<{ username:string }>}) {const {username }=await params// Initiate requestsconstartistData=getArtist(username)constalbumsData=getAlbums(username)const [artist,albums]=awaitPromise.all([artistData, albumsData])return (    <>      <h1>{artist.name}</h1>      <Albumslist={albums} />    </>  )}

Good to know: If one request fails when usingPromise.all, the entire operation will fail. To handle this, you can use thePromise.allSettled method instead.

Preloading data

You can preload data by creating a utility function that you eagerly call above blocking requests.<Item> conditionally renders based on thecheckIsAvailable() function.

You can callpreload() beforecheckIsAvailable() to eagerly initiate<Item/> data dependencies. By the time<Item/> is rendered, its data has already been fetched.

app/item/[id]/page.tsx
import { getItem, checkIsAvailable }from'@/lib/data'exportdefaultasyncfunctionPage({  params,}: {  params:Promise<{ id:string }>}) {const {id }=await params// starting loadingitem datapreload(id)// perform another asynchronous taskconstisAvailable=awaitcheckIsAvailable()return isAvailable? <Itemid={id} />:null}constpreload= (id:string)=> {// void evaluates the given expression and returns undefined// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/voidvoidgetItem(id)}exportasyncfunctionItem({ id }: { id:string }) {constresult=awaitgetItem(id)// ...}

Additionally, you can use React'scache function and theserver-only package to create a reusable utility function. This approach allows you to cache the data fetching function and ensure that it's only executed on the server.

utils/get-item.ts
import { cache }from'react'import'server-only'import { getItem }from'@/lib/data'exportconstpreload= (id:string)=> {voidgetItem(id)}exportconstgetItem=cache(async (id:string)=> {// ...})

API Reference

Learn more about the features mentioned in this page by reading the API Reference.

Was this helpful?

supported.

[8]ページ先頭

©2009-2025 Movatter.jp