
Software Architecture
Understand Server and Client Component boundaries in Next.js to reduce browser JavaScript without losing interactivity.
One misplaced "use client" directive can turn Next.js's biggest advantage into a heavier React SPA.
That does not make "use client" bad. The issue is treating it as a quick fix whenever a component needs state, an event handler, or a browser API. Put it in page.tsx, layout.tsx, or an overly broad parent component, and the client boundary grows further than necessary.
In the App Router, components are Server Components by default. They can fetch data on the server and use server-only dependencies without sending that code to the browser. When a file has "use client", that file and the dependencies it imports become part of the client-side graph.
Imagine a product page with a title, product data, long-form content, and search filters. Because the filter needs useState, the easiest move appears to be making the entire page a Client Component.
"use client";
export default function ProductsPage() {
const [query, setQuery] = useState("");
return (
<>
<SearchFilters query={query} onQueryChange={setQuery} />
<ProductGrid query={query} />
</>
);
}It works, but the page is now a client boundary. Layout, content, formatters, and the product grid can be pulled toward the browser more than the experience actually requires.
Keep the page and data on the server. Extract only the part that truly needs the browser into a Client Component. The filter stays interactive in the browser, while the product grid, data, and remaining content can render on the server.
export default async function ProductsPage({ searchParams }) {
const products = await getProducts(searchParams.query);
return (
<>
<SearchFilters />
<ProductGrid products={products} />
</>
);
}"use client";
export function SearchFilters() {
// state, event handlers and URL interaction live here
}Use client does not mark an interactive component. It marks the boundary where the browser must participate.
Real-time dashboards, rich-text editors, drag-and-drop boards, and internal tools with complex client state will need more Client Components. The goal is not to push everything to the server. The goal is to keep browser JavaScript where it creates value.
Next.js is not powerful because everything becomes fast automatically. It is powerful when a team knows what belongs on the server, what belongs in the browser, and does not let one interactive button pull an entire page into the client graph. Before adding use client, ask: does this code truly need to run in the browser, or is it simply near code that does?