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.
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
Fullstack developer based in the Philippines, working mostly with Laravel and Vue.js, with eight years of production experience across web and mobile.
More on Frontend
September 17, 2025
Next.js Metadata API: SEO Setup That Actually Works
Next.js 16's metadata API gives you typed control over titles, descriptions, Open Graph, and structured data. Here's a complete setup that holds up to real SEO.
September 10, 2025
Next.js Image Optimization: What You Need to Know
Next.js's Image component handles formats, sizes, and lazy loading automatically. Here's what to set, what to skip, and the gotchas.
September 3, 2025
Organizing Tailwind CSS in Larger Projects
Tailwind starts simple and stays manageable in big codebases — if you set a few conventions early. Here's a structure that scales.