TechLead
React
February 8, 20269 min read

React Server Components vs. Client Components: When to Use Which

A practical guide to choosing between Server Components and Client Components in React and Next.js. Understand the rendering model, performance implications, and real-world patterns for 2026.

By TechLead
React
Next.js
Server Components
Performance
RSC

React Server Components (RSC) fundamentally changed how we think about rendering in React. Instead of shipping all your component code to the browser, Server Components run exclusively on the server, sending only their rendered output to the client. In Next.js App Router, every component is a Server Component by default.

1. The Mental Model

Think of your component tree as having two zones:

  • Server Zone (default): Components that fetch data, access databases, read files, or render static content. They produce HTML on the server and send zero JavaScript to the browser.
  • Client Zone ("use client"): Components that need interactivity — event handlers, state, effects, browser APIs. These hydrate in the browser.

2. When to Use Server Components

Use Server Components when your component:

  • Fetches data from a database or API
  • Accesses server-only resources (file system, environment variables)
  • Renders static or read-only content
  • Has large dependencies you don't want in the client bundle
// app/blog/page.tsx — Server Component (default)
import { getBlogPosts } from "@/lib/posts";

export default async function BlogPage() {
  const posts = await getBlogPosts(); // Direct DB access

  return (
    <main>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </main>
  );
}

3. When to Use Client Components

Add "use client" at the top of a file when your component needs:

  • State: useState, useReducer
  • Effects: useEffect, useLayoutEffect
  • Event handlers: onClick, onChange, onSubmit
  • Browser APIs: window, localStorage, IntersectionObserver
  • Custom hooks that use any of the above
"use client";
import { useState } from "react";

export default function SearchBar() {
  const [query, setQuery] = useState("");

  return (
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Search..."
    />
  );
}

4. The Composition Pattern

The key insight: Client Components can render Server Components as children. This lets you keep interactivity at the leaves while the majority of your tree stays on the server:

// Server Component (parent)
import InteractiveWidget from "./InteractiveWidget";

export default async function Dashboard() {
  const data = await fetchData();

  return (
    <InteractiveWidget>
      {/* These children are Server Components */}
      <DataTable data={data} />
      <Chart data={data} />
    </InteractiveWidget>
  );
}

5. Performance Comparison

MetricServer ComponentClient Component
JS Bundle Size0 KB addedComponent code shipped to browser
Time to InteractiveInstant (HTML only)Requires hydration
Data FetchingDirect server accessAPI calls from browser
SEOFully rendered HTMLDepends on SSR/hydration
InteractivityNoneFull interactivity

6. Common Mistakes to Avoid

  • Don't mark everything as "use client". Only the components that truly need interactivity.
  • Don't import Server Components into Client Components. Pass them as children instead.
  • Don't use useEffect for data fetching when a Server Component with async/await works better.
  • Don't pass non-serializable props (functions, classes) from Server to Client boundaries.

Related Articles