DEV Community

Tihomir Ivanov
Tihomir Ivanov

Posted on

Server and Client Components in React & Next.js

Here is a 5-minute read article to remind you about Server and Client components—perfect for quick review before interviews or when writing modern React apps.

React and Next.js 14+ introduced Server Components (RSC) and Client Components to optimize rendering and performance. This architecture allows developers to choose where components execute—on the server or in the browser—resulting in smaller bundles, faster loads, and cleaner architecture.


Rendering Environments

React separates logic into two environments:

  • Client (Browser) — For interactivity, state, and DOM effects
  • Server (Node.js) — For data fetching, caching, and secure logic

Use "use client" and "use server" directives to explicitly control this boundary.


Server Components

Benefits

  • Data Fetching on the server
  • Security by hiding sensitive logic
  • Caching and performance optimization
  • SEO & Shareability
  • Streaming: progressive loading via chunks
  • Smaller JS bundle

Default in Next.js

Next.js uses Server Components by default. Rendering flow:

New Request
   ↓
Server renders Server Components → RSC Payload → Sent to client
   ↓
Client renders UI & hydrates Client Components
Enter fullscreen mode Exit fullscreen mode

Client Components

Use when you need:

  • Interactivity (useState, useEffect, DOM events)
  • Access to Browser APIs (e.g., localStorage, geolocation)

Place "use client" at the top of the component.


Composition Patterns

Sharing Data

Use fetch or React.cache() in Server Components to fetch and reuse data.

Client Components Down the Tree

Move Client Components as deep as possible to minimize bundle size.

Props Serialization

Pass props from Server → Client via serialization (only serializable values!).


Rendering Strategies

1. Static Rendering (default)

  • Cached at build or revalidated in background
  • Great for non-personalized pages (e.g., blog posts, product listings)

2. Dynamic Rendering

  • Used when data is personalized (cookies, headers, searchParams)
  • Rendered per-request

Switching to Dynamic Rendering

Uses Dynamic API Data Cached Resulting Strategy
No Yes Static Rendering
Yes Yes Dynamic Rendering (cached)
No No Dynamic Rendering
Yes No Dynamic Rendering

Streaming

  • Streams UI from server as chunks become ready
  • Built into Next.js App Router using Server Actions + React Suspense

Example

Server Component

// app/page.tsx
import { fetchProducts } from "@/lib/data";

export default async function Page() {
  const products = await fetchProducts(); // Server-side
  return (
    <div>
      <h1>Our Products</h1>
      <ProductList products={products} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Client Component

// components/ProductList.tsx
"use client";
import { useState } from "react";

export default function ProductList({ products }) {
  const [highlight, setHighlight] = useState(false);

  return (
    <ul onMouseEnter={() => setHighlight(true)}>
      {products.map(p => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

Diagram – RSC Payload Rendering

New Request
   ↓
[ Server ]
Render Server Components
   ↓
RSC Payload (binary format)
   ↓
[ Client ]
1. Hydrate HTML
2. Reconcile Server and Client Components
3. Activate JS for interactive Client Components
Enter fullscreen mode Exit fullscreen mode

RSC Payload is a compact binary format sent from server → client with the rendered component tree.


Summary

  • Use Server Components for logic, data, and performance
  • Use Client Components only for interactivity
  • Pass only serializable props across boundaries
  • Stream content progressively when possible

Top comments (0)