Server by default, client when needed

In the Next.js App Router, components are server components by default. That means they can fetch data on the server and send rendered output to the client without shipping their component code to the browser.

Use client components when you need browser APIs, event handlers, local interactive state, or effects.

ts
// Server component
export default async function Page() {
  const posts = await getPosts();
  return <PostList posts={posts} />;
}

Client components are islands of interactivity

A client component is not bad. It is simply more expensive and more capable in the browser. The goal is to keep the client boundary around the part that needs interactivity, not around the entire page by habit.

ts
"use client";

export function LikeButton() {
  const [liked, setLiked] = useState(false);
  return <button onClick={() => setLiked(true)}>Like</button>;
}

Try the idea

Server/client boundaryLikeButton
Server componentFetch posts, render article, prepare static content
Client islandLikeButton: browser events and local state

What not to put in client components

Avoid moving database calls, secrets, heavy server-only formatting, and static article content into client components only because one small button needs state.

Production notes for React apps

React code becomes fragile when boundaries are vague. Before adding state, effects, or client components, decide what owns the data and what actually needs to run in the browser.

A production-ready React feature should be readable from the data boundary down to the interaction boundary. If every component knows everything, future changes become expensive.

Conclusion

Server Components are a way to make boundaries explicit. Use them to keep static and data-heavy work on the server while preserving focused client interactivity.