Data Fetching in Next.js Server Components: The Basics

Server Components can be async and await data directly. Learn the core pattern for fetching data on the server in the Next.js App Router.

7 min read

Data fetching in Next.js Server Components starts with one rule: a Server Component can be an async function, so it can await data and render the result directly. Instead of fetching inside a client-side effect after the page loads, a page fetches on the server and sends finished HTML to the browser.

App.tsxApp.tsx
// app/posts/page.tsx
type Post = { id: number; title: string };
 
export default async function PostsPage() {
  const res = await fetch("https://api.example.com/posts");
  const posts: Post[] = await res.json();
  return <ul>{posts.map((post) => <li key={post.id}>{post.title}</li>)}</ul>;
}

When you visit the posts route, the list arrives already filled with titles. The fetch runs on the server before any HTML is sent, so the browser never runs this code and the fetching logic stays out of the client bundle.

Why fetch on the server

Fetching in a Server Component removes a whole class of loading patterns. There is no mounted state, no effect, and no spinner that appears after the first paint. The server waits for the data, then sends the rendered list.

The same page can also read a database or the filesystem directly, because Server Components run in a secure environment where credentials never ship to the client. Direct database access follows the same idea, applied to a query instead of an HTTP call.

Fetching on the server also shortens the request chain. Instead of the browser asking for a page and then the page asking for data, the server fetches both and hands back one combined response.

Loading and error states

An async page that waits for a slow request holds up the whole route unless you give Next.js something to show in the meantime. Two file conventions handle this: a loading file for the wait, and an error boundary for the failure.

App.tsxApp.tsx
// app/posts/loading.tsx
export default function Loading() {
  return <p aria-live="polite">Loading posts</p>;
}

Next.js shows this component while the page is still fetching, then swaps it for the finished list automatically. If the fetch throws instead, an error file catches it so the route does not blank out.

A route can have both files, so a slow page shows one fallback and a broken page shows the other. This matters because a fetch is not cached by default in Next.js 16, so an uncached request blocks the page until it resolves unless something covers the wait. How partial content streams in over time is covered in streaming in Next.js.

Common mistake

This pattern only works in Server Components. Marking the same component with the use client directive and keeping the async keyword throws at runtime instead:

texttext
async/await is not yet supported in Client Components, only Server Components.

The fix depends on why the component needs to run in the browser. If it does not need browser APIs, remove the directive and let it stay a Server Component.

If it genuinely belongs in the browser, fetch on the server and pass the data down as props, or use a client data library such as SWR or TanStack Query. The complete decision tree is covered in how to fetch data without useEffect.

Rune AI

Rune AI

Key Insights

  • Server Components can be async and await data directly.
  • The fetch runs on the server and never enters the client bundle.
  • A loading file shows a fallback while a page waits for data.
  • An error boundary catches a failed fetch so the route does not blank.
  • Client Components cannot be async, so they fetch through a different pattern.
RunePowered by Rune AI

Frequently Asked Questions

Can every Server Component use async and await?

Yes. Pages, layouts, and nested Server Components can all be async and await data. The only exception is a Client Component, which cannot be an async function.

Does the fetch run on the server or in the browser?

On the server. The browser receives rendered HTML and never executes the fetching code.

What should I show while the data is loading?

Add a loading file so Next.js can display a fallback, and an error boundary so a failed fetch does not blank the route.

Conclusion

Fetching data in a Server Component means making the component async and awaiting the request directly. The data loads on the server, keeps secrets out of the client, and pairs with loading and error files to handle slow or failed requests.