How to fetch and stream data
This page will walk you through how you can fetch data in Server Components and Client Components. As well as how to to stream content that depends on data.
Fetching data
Server Components
You can fetch data in Server Components using:
- The
fetch
API - An ORM or database
With the fetch
API
To fetch data with the fetch
API, turn your component into an asynchronous function, and await the fetch
call. For example:
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
With an ORM or database
You can fetch data with an ORM or database by turning your component into an asynchronous function, and awaiting the call:
import { db, posts } from '@/lib/db'
export default async function Page() {
const allPosts = await db.select().from(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
Client Components
You can use React's use
hook to stream data from the server to client. Start by fetching data in your Server component, and pass the promise to your Client Component as prop:
import Posts from '@/app/ui/posts
import { Suspense } from 'react'
export default function Page() {
// Don't await the data fetching function
const posts = getPosts()
return (
<Suspense fallback={<div>Loading...</div>}>
<Posts posts={posts} />
</Suspense>
)
}
Then, in your Client Component, use the use
hook read the promise:
'use client'
import { use } from 'react'
export default function Posts({ posts }) {
const posts = use(posts)
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
In the example above, you need to wrap the <Posts />
component in a <Suspense>
boundary. This means the fallback will be shown while the promise is being resolved. Learn more about streaming.
Alternatively, you can use a community library like SWR or React Query. These libraries have their own semantics for caching, streaming, and other features. For example:
'use client'
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((r) => r.json())
export default function BlogPage() {
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 }) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
Streaming
Warning: The content below assumes the
dynamicIO
config option is enabled in your application. The flag was introduced in Next.js 15 canary.
When using async/await
in Server Components, Next.js will opt into dynamic rendering. This means the data will be fetched and rendered on the server for every user request. If there are any slow data requests, the whole route will be blocked from rendering.
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.
There are two ways you can implement streaming in your application:
- With the
loading.js
file - With React's
<Suspense>
component
With loading.js
You can create a loading.js
file in the same folder as your page to stream the entire page while the data is being fetched. For example, to stream app/blog/page.js
, add the file inside the app/blog
folder.
export default function Loading() {
// Define the Loading UI here
return <div>Loading...</div>
}
On navigation, the user will immediately see the layout and a loading state while the page is being rendered. The new content will then be automatically swapped in once rendering is complete.
Behind-the-scenes, loading.js
will be nested inside layout.js
, and will automatically wrap the page.js
file and any children below in a <Suspense>
boundary.
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.
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
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>
{/* Any content wrapped in a <Suspense> boundary will be streamed */}
<Suspense fallback={<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 the React Devtools.
Next Steps
Was this helpful?