Next.js 16 Server Components: A Practical Mental Model

Server components changed how data flows in Next.js. Here's a mental model that makes them click — async pages, streaming, and where 'use client' belongs.

Richard GamoraRichard GamoraFullstack developer·5 min read
Next.jsReactServer Components

Server components are the biggest change to React in years, and they confuse most developers on first contact. Once the mental model clicks, they make a lot of code simpler. Here is the model that worked for me.

By default, components run on the server

In Next.js's app directory, every component is a server component unless it opts out with 'use client'. Server components render to HTML on the server and do not ship JavaScript to the browser. They can be async, fetch data directly, and access server-only resources (env vars, databases, secrets).

Use client when you need interactivity

Client components are the React you already know — useState, useEffect, event handlers. The 'use client' directive at the top of a file marks it (and any component imported from it that has its own components) as client. The boundary is at the import — server components can import client components, but not the other way around.

Where to put 'use client'

Put it as deep in the tree as possible. A page can be a server component that fetches data and renders mostly server children. The interactive bits (a search input, a counter, a modal) become client components imported into the server tree.

Pushing 'use client' up to the page level works but ships the whole page as JavaScript — defeats the point. Most real apps end up with a few small client components in a sea of server ones.

Async data, no useEffect

tsx// app/posts/page.tsx — a server component
import { db } from '@/lib/db'

export default async function Posts() {
  const posts = await db.post.findMany()
  return <PostList posts={posts} />
}

No useEffect, no loading flag, no client-side waterfall. The data is awaited server-side and the rendered HTML includes it. The simplification is the win — most data fetching becomes async/await on the server, and useEffect goes away from the places it never belonged.

About the author

Richard Gamora

Richard Gamora

Fullstack developer based in the Philippines, working mostly with Laravel and Vue.js, with eight years of production experience across web and mobile.

me@richardgamora.comUpwork ↗

More on Frontend