Script Valley
Next.js: Full-Stack React Applications
Next.js Foundations and Project SetupLesson 1.5

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.

How to fetch data in Next.js Server Components โ€” Next.js Foundations and Project Setup โ€” Next.js: Full-Stack React Applications โ€” Script Valley โ€” Script Valley