Skip to content

Understanding React Server Components

Aug 202410 min read

React Server Components (RSC) change how you think about where React code runs. If you have spent years treating React as something that always ends up in the browser, this feels like a reset. Some components stay on the server forever, and that opens up a different set of tradeoffs.

What Are React Server Components?

Server Components run only on the server. They render to a format React can stream to the client, but the component code itself never ships to the browser. That means no client bundle cost for those components.

// This is a Server Component by default in Next.js 13+
async function BlogPost({ id }) {
  // Direct database access
  const post = await db.posts.findById(id);

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

Why People Care

1. Less JavaScript on the Client

Server Components do not add to your client bundle, which usually means:

  • Faster initial loads
  • Better performance on slower devices
  • Less bandwidth used

2. Direct Access to Backend Data

You can talk to your database, file system, or internal APIs without setting up a separate client fetch layer.

async function UserProfile({ userId }) {
  const user = await db.users.findById(userId);
  const posts = await db.posts.where({ authorId: userId }).fetch();

  return (
    <div>
      <h2>{user.name}</h2>
      <PostList posts={posts} />
    </div>
  );
}

3. Automatic Code Splitting

Every Server Component is already split from the client bundle. You do not have to think about it as much.

Server vs Client Components

Scroll horizontally to view all columns

FeatureServer ComponentsClient Components
Runs onServer onlyServer + Client
Can use hooksNoYes
Can access backendYesNo
Adds to bundleNoYes
Can be asyncYesNo

Common Patterns

Data Fetching

// Server Component - fetches data directly
async function ProductList() {
  const products = await fetchProducts();

  return (
    <div>
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

Composition

// Mix Server and Client Components
import ClientCounter from "./ClientCounter"; // 'use client'

async function Dashboard() {
  const stats = await getStats();

  return (
    <div>
      <h1>Dashboard</h1>
      <StaticContent data={stats} />
      <ClientCounter initialValue={stats.count} />
    </div>
  );
}

Challenges and Things to Watch

Props Serialization

Props passed from Server to Client Components must be serializable.

// Will not work - functions are not serializable
<ClientComponent onClick={handleClick} />

// Works - pass data, handle events in client
<ClientComponent data={data} />

Context Limitations

Server Components cannot read React Context. If you need context, you either pass data down or move that part into a Client Component.

Migration Strategy

If you are moving an existing app over, this is a safe order:

  1. Start with layouts and static pages
  2. Move data fetching into Server Components
  3. Keep interactive pieces as Client Components
  4. Optimize based on real performance data

The Bigger Shift

RSC is not just another API. It changes the architecture. You get clearer boundaries between server and client code, simpler data paths, and a better default performance story. It does take a mental shift, but it also cleans up a lot of old patterns.

Conclusion

React Server Components are a major shift in the React ecosystem. If you are building with Next.js today, you can start with small pieces and grow from there. The value shows up when you keep the client bundle lean and let the server do the heavy lifting.

Related Posts