Data Fetching in Next.js
Learn the core patterns for fetching data in Server Components — the foundation every rendering strategy builds on.
Fetching Directly in Server Components
Because Server Components can be async functions, you fetch data with a plain await, right where you use it — no useEffect, no loading state variable, no separate data-fetching library required for the basic case.
export default async function OrdersPage() { const res = await fetch('https://api.example.com/orders'); const orders = await res.json();
return ( <ul> {orders.map(o => <li key={o.id}>{o.item}</li>)} </ul> );}Sequential vs Parallel Fetching
Awaiting one fetch after another runs them sequentially, adding their times together. When requests don't depend on each other, start them together and await both, so they run in parallel instead.
Sequential (slower)
const user = await getUser(id); // waitsconst orders = await getOrders(id); // then waits again// total time ≈ getUser + getOrdersParallel (faster)
const [user, orders] = await Promise.all([ getUser(id), getOrders(id),]);// total time ≈ the SLOWER of the two, not the sumFetching Without an API — Direct Database Access
Because Server Components run only on the server, they can talk directly to a database — no API endpoint required as a middleman, since this code never reaches the browser.
import { db } from '@/lib/db';
export default async function UsersPage() { const users = await db.user.findMany(); return ( <ul> {users.map(u => <li key={u.id}>{u.name}</li>)} </ul> );}This is one of the biggest practical wins of Server Components: a page can query a database directly, skipping the API layer entirely for internal data needs.
Passing Fetched Data to Client Components
Fetch once in the Server Component, then pass the result down as a prop to any Client Component that needs to make it interactive — this avoids duplicating the fetch on the client.
// page.tsx (Server Component)import FilterableList from './FilterableList'; // Client Component
export default async function Page() { const items = await getItems(); return <FilterableList items={items} />;}Common Beginner Mistakes
This adds their durations together — use Promise.all for requests that don't depend on each other's results.
This duplicates the request and adds an unnecessary loading state — just use the prop that was already fetched on the server.
FAQs
Less often for initial page data, since Server Components cover that — but they're still valuable for client-side data that needs polling, refetching on focus, or optimistic updates after the page has loaded.
Yes, but you lose Next.js's automatic caching behavior tied to fetch — using the native fetch is what enables the caching and revalidation features covered in the next lesson.
Summary
Server Components let you fetch data with plain async/await, run independent requests in parallel, and even talk to a database directly. Next, you'll learn exactly how Next.js caches these fetch calls.