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/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/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:
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
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.
Frequently Asked Questions
Can every Server Component use async and await?
Does the fetch run on the server or in the browser?
What should I show while the data is loading?
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.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.