Frontend

Modern React: Server Components Explained

By Mohd Baquir Qureshi
React Code

For years, React development meant building Single Page Applications (SPAs) where the browser downloads a massive JavaScript bundle, executes it, and renders the UI. This led to slow initial load times and poor SEO. Server-Side Rendering (SSR) helped, but it was complex. Enter React Server Components (RSC).

What are React Server Components?

React Server Components allow you to write components that render exclusively on the server. They never ship their JavaScript to the client browser. They execute, fetch their data, generate HTML/UI instructions, and only send the final rendered output over the wire.

The Problem RSC Solves

Imagine a typical React component that fetches data:

// Traditional Client Component
import { useEffect, useState } from 'react';

export default function UserProfile({ id }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${id}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [id]);

  if (!user) return <div>Loading...</div>;
  return <div>{user.name}</div>;
}

The flaws here:

  1. The browser has to download React, download your component code, parse it, and execute it before it even starts fetching the data.
  2. You have to build and maintain a separate /api/users endpoint on your backend.
  3. The user sees a loading spinner.

The Server Component Way

With RSC (most commonly used in Next.js App Router), that same component becomes asynchronous and runs directly on the server:

// React Server Component
import db from '@/lib/db';

export default async function UserProfile({ id }) {
  // Direct database access! No API endpoint needed.
  const user = await db.user.findUnique({ where: { id } });

  return <div>{user.name}</div>;
}

Why this is revolutionary:

  • Zero Bundle Size: The code for db.user.findUnique never goes to the browser. You can import massive libraries (like a markdown parser or a database ORM) and the user's bundle size increases by exactly zero bytes.
  • Direct Backend Access: You can read from a database, the filesystem, or internal microservices directly inside your component.
  • Faster Loading: The server is closer to the database than the user's phone. Data fetching is near-instant, and the user receives fully rendered HTML immediately.

Client Components vs. Server Components

Server components are the new default, but they have limitations: they cannot use state (useState), lifecycle hooks (useEffect), or browser APIs (like window or event listeners like onClick).

For interactive UI, you still need Client Components. You declare them by adding "use client" at the top of the file.

"use client"
import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

The Interleaved Tree

The beauty of RSC is that you can interleave them. You can pass a Server Component as a children prop to a Client Component. This allows you to have a highly interactive layout (like a collapsible sidebar with state) that wraps a purely static, server-rendered content area.

Conclusion

React Server Components represent a paradigm shift. By moving the heavy lifting back to the server, React is combining the performance and SEO benefits of traditional server-rendered apps (like PHP or Rails) with the rich interactivity of modern SPAs.