Fetch in Server Components
Read data on the server and pass it to Client Components as props.
From next-best-practices, data-patterns.md.
Time to data
Press Load page, then drag the sliders.
Client fetch on mountBad
- Ada Lovelace
- Grace Hopper
- Alan Turing
useEffect(() => fetch('/api/users'))Server ComponentGood
- Ada Lovelace
- Grace Hopper
- Alan Turing
await db.user.findMany()// app/users/page.tsx
async function UsersPage() {
const users = await db.user.findMany();
return <UserList initialData={users} />;
}
// user-list.tsx
'use client';
function UserList({ initialData }) {
const [users, setUsers] = useState(initialData);
}
Where the key ends up
Type a key, then switch tabs.
Client calls the APIBad
Server
Browser
NEXT_PUBLIC_WEATHER_KEYServer calls the APIGood
Server
Browserrendered HTML
process.env.WEATHER_KEY// app/forecast/page.tsx
async function ForecastPage() {
const days = await fetch('https://api.weather.dev/v1/forecast?city=Lisbon', {
headers: { authorization: `Bearer ${process.env.WEATHER_KEY}` },
}).then(r => r.json());
return <Forecast initialData={days} />;
}
Which pattern?
Answer the questions.
Need to fetch data? ├── From a Server Component? │ └── Use: Fetch directly (no API needed) ├── From a Client Component? │ ├── Is it a mutation (POST/PUT/DELETE)? │ │ └── Use: Server Action │ └── Is it a read (GET)? │ └── Use: Route Handler OR pass from Server Component ├── Need external API access (webhooks, third parties)? │ └── Use: Route Handler └── Need REST API for mobile app / external clients? └── Use: Route Handler
Why it matters, pros and cons
Why it matters
The server sits next to the data, so rows ship in the first HTML.
Pros
- No API route to maintain.
- No client-server waterfall.
- Secrets stay on the server.
Cons
- Live data (polling, infinite scroll) still needs client fetches.
- Mobile apps and webhooks need a Route Handler.
- A slow query blocks the page without
<Suspense>.