Loading server data
At 7 am Ama opens the stock screen on her store's slow connection at the edge of town. This lesson shows you how to load its data on the server, show something useful at once, recover from errors, cache what can be cached and keep the live order counts fresh, with React 19 and the Next.js 16 App Router.
By the end you can
- Fetch data in async Server Components and keep API keys and heavy code out of the browser
- Place the
"use client"boundary and pass only serialisable props across it - Spot a request waterfall and fix it with parallel requests,
Promise.alland early starts - Give a route instant loading, error and not-found states with
loading.tsx, Suspense,error.tsxandnotFound() - Decide how fresh each piece of data must be: cache it with
"use cache", refresh it after writes, or poll it every 30 seconds
The everyday picture
Think of a busy restaurant kitchen. The pantry, the sharp knives and the secret sauce recipe stay in the kitchen, and guests never see them. The cooks prepare the food there, and the waiter carries out each dish as soon as it is ready: bread first, the curry a few minutes later, so nobody stares at an empty table. A good cook puts the rice on and starts chopping onions at the same time, instead of waiting for the rice to finish. Some things are made ahead: a pot of stock cooked in the morning is good all day, while the fish is cooked to order. And on the table there is a small bell, the one thing the guest controls.
Loading data in Next.js works the same way. Server Components are the kitchen: they hold the secrets and do the heavy work. Streaming is the waiter bringing parts of the page as they are ready. Starting requests together is the cook who does not wait for the rice. Caching is the morning pot of stock. And a Client Component is the bell on the table: only the interactive parts travel to the browser.
Server Components: fetch where the data lives
In the Next.js App Router, every component is a Server Component unless you mark it otherwise. It runs on the server and never in the browser. Because of that it can be an async function: you await your data right in the component body, from fetch, a database client or your own API, and return JSX that uses it.
// app/stores/[storeId]/stock/page.tsx: a Server Component (the default)
export default async function StockPage({ params }: { params: Promise<{ storeId: string }> }) {
const { storeId } = await params // params is a promise since Next.js 15
const res = await fetch(`${process.env.STOCK_API_URL}/api/stores/${storeId}/stock`, {
headers: { Authorization: `Bearer ${process.env.STOCK_API_KEY}` }, // stays on the server
})
if (!res.ok) throw new Error(`Stock API answered ${res.status}`)
const items: StockItem[] = await res.json()
return <StockTable items={items} />
}Three good things follow. Secrets stay on the server: Next.js only copies environment variables that start with NEXT_PUBLIC_ into browser code, so STOCK_API_KEY never reaches Ama's PC. Big libraries stay on the server: a date library used only while rendering adds nothing to the JavaScript an old store PC must download. The result streams: React sends HTML for the first paint, plus an RSC payload the browser uses to fit the result into the page and to navigate later without a full reload. The server also sits close to the API, so each request costs milliseconds instead of a slow trip over the store's mobile data (Speed numbers and back-of-the-envelope math).
A Server Component has no state, no effects and no event handlers: no useState, no useEffect, no onClick. Anything that reacts to typing, clicks or a timer needs a Client Component. To make sure a data file never reaches the browser by accident, put import 'server-only' at its top (from the small server-only package): the build then fails if a Client Component imports it.
Quick check
Which of these can a Server Component do?
A Server Component can be async and read data directly, because it runs on the server. It has no state and no event handlers: it renders once per request and sends the result, so typing and clicks belong in a Client Component.
OrderSummary is a Client Component; it asks a small route handler for fresh order counts every 30 seconds, and the route handler adds the key on the server.