How to fetch data in Next.js Server Components
async Server Components, fetch API extensions, request memoization, revalidation, static vs dynamic data fetching, cache options
Async Components Are Native
Server Components can be async functions. You fetch data directly inside the component โ no useEffect, no loading state, no API client setup. The component waits for the data, then renders.
// app/posts/page.tsx
export default async function PostsPage() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts')
const posts = await res.json()
return (
<ul>
{posts.slice(0, 5).map((post: { id: number; title: string }) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}Controlling Cache Behavior
Next.js extends the native fetch API with a cache option and next.revalidate:
// Static โ cached forever (default)
fetch(url)
// Revalidate every 60 seconds (ISR)
fetch(url, { next: { revalidate: 60 } })
// Dynamic โ never cached, always fresh
fetch(url, { cache: 'no-store' })Request Memoization
If you call the same fetch URL in multiple Server Components during the same render, Next.js automatically deduplicates the requests. Only one actual HTTP request fires. This means you can fetch the same data in a layout and a page without worrying about duplicate network calls โ a major architectural advantage over the Pages Router's getServerSideProps.
For database queries (not using fetch), use React's cache() function to get the same deduplication behavior.
