The biggest mental shift in Next.js is that not every component needs to run in the browser.
Beginners often add "use client" too quickly because browser interactivity feels familiar, but that can throw away many of the framework benefits.
Professionals use server components by default and introduce client components only where state, effects, or browser APIs are truly needed.
This lesson matters because the server-versus-client boundary affects bundle size, security, and data flow all at once.
Many learners think server components are only for data fetching, but the idea is broader. If a piece of UI can be prepared without browser-only APIs or live client state, it can often remain on the server. That means less JavaScript is sent to the user.
A common beginner mistake is adding "use client" at the top of a parent component and accidentally turning a large part of the tree into client-side code when only one small button needed it.
Professionals often keep page shells, lists, and read-only data rendering on the server, then embed smaller client islands for search bars, filters, modals, or live controls. This creates a better balance between speed and interaction.
The question is not "can this be a client component?" The better question is "what is the minimum interactive surface that needs client behavior?" That framing usually produces cleaner code and smaller bundles.
Server components cannot use browser APIs, event handlers, or React hooks like useState and useEffect. Client components can, but they add bundle weight and hydration cost. Understanding this tradeoff is more important than memorizing the syntax.
The best developers treat the boundary as an architectural decision. It changes how fast the page loads, how easy the code is to test, and what logic stays private on the server.
This split is one of the most practical patterns to understand early.
export default async function ProductsPage() {
const products = await getProducts();
return (
<section>
<h1>Products</h1>
<ProductFilters />
<ProductList products={products} />
</section>
);
}
// ProductFilters.tsx
'use client';
export function ProductFilters() {
return <button>Open filters</button>;
}
Yes. That is a normal pattern. The server component can provide data and structure while the child client component handles interaction.
No. Use them when the browser really needs to manage state, effects, or user interaction. The goal is not zero client code; the goal is intentional client code.
Explore 500+ free tutorials across 20+ languages and frameworks.