Table of Contents

Share this post

React Server Components: The Future of React
Frontend··5 min read·11,259 views

React Server Components: The Future of React

Understanding React Server Components and how they're changing the way we build React applications.

React Server Components: The Future of React

React Server Components (RSC) represent a fundamental shift in how we think about React applications. Let's explore what they are and why they matter.

What Are Server Components?

Server Components are React components that run only on the server. They never ship JavaScript to the client.

// This is a Server Component (default in Next.js 13+)
async function BlogPost({ slug }) {
  const post = await db.posts.findOne({ slug });
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

Key benefits:

  • Zero client JS: Smaller bundle sizes
  • Direct database access: No API needed
  • Automatic code splitting: Only interactive code ships to client

Server vs Client Components

Server Components (default)

// app/Post.jsx
async function Post() {
  const data = await fetch('...');
  return <div>{data.title}</div>;
}

Can:

  • Use async/await
  • Access backend resources directly
  • Use Node.js APIs

Cannot:

  • Use state or effects
  • Use browser APIs
  • Add event listeners

Client Components

'use client'; // Opt-in directive

import { useState } from 'react';

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

Can:

  • Use hooks (useState, useEffect, etc.)
  • Use browser APIs
  • Add interactivity

Must:

  • Ship JavaScript to client
  • Be marked with 'use client'

Composition Patterns

Server Component with Client Children

// Server Component
async function Page() {
  const post = await getPost();
  
  return (
    <div>
      <h1>{post.title}</h1>
      {/* Client component for interactivity */}
      <LikeButton postId={post.id} />
      <Comments postId={post.id} />
    </div>
  );
}

// Client Component
'use client';
function LikeButton({ postId }) {
  const [liked, setLiked] = useState(false);
  return <button onClick={() => setLiked(!liked)}>
    {liked ? '❤️' : '🤍'}
  </button>;
}

Passing Server Components as Props

// Client Component that accepts children
'use client';
function Tabs({ children }) {
  const [tab, setTab] = useState(0);
  return <div>{children[tab]}</div>;
}

// Server Component
async function Page() {
  const data1 = await getData1();
  const data2 = await getData2();
  
  return (
    <Tabs>
      <TabContent data={data1} />
      <TabContent data={data2} />
    </Tabs>
  );
}

Data Fetching

Parallel Fetching

async function Page() {
  // These run in parallel
  const [posts, users] = await Promise.all([
    getPosts(),
    getUsers()
  ]);
  
  return <Layout posts={posts} users={users} />;
}

Sequential Fetching

async function Post({ id }) {
  const post = await getPost(id);
  const author = await getAuthor(post.authorId);
  
  return <PostCard post={post} author={author} />;
}

Streaming with Suspense

import { Suspense } from 'react';

function Page() {
  return (
    <div>
      <h1>My Page</h1>
      <Suspense fallback={<Skeleton />}>
        <SlowComponent />
      </Suspense>
    </div>
  );
}

async function SlowComponent() {
  const data = await slowDataFetch();
  return <div>{data}</div>;
}

Caching Strategies

Next.js provides automatic caching:

// Cached by default
async function getData() {
  const res = await fetch('https://api.example.com/data');
  return res.json();
}

// Revalidate every hour
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    next: { revalidate: 3600 }
  });
  return res.json();
}

// No caching
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'no-store'
  });
  return res.json();
}

Real-World Example: Blog

// app/blog/[slug]/page.jsx (Server Component)
async function BlogPost({ params }) {
  const post = await db.posts.findOne({ slug: params.slug });
  const related = await db.posts.find({ 
    category: post.category 
  }).limit(3);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <PostContent content={post.content} />
      <LikeButton postId={post.id} />
      <CommentSection postId={post.id} />
      <RelatedPosts posts={related} />
    </article>
  );
}

// Client Component
'use client';
function LikeButton({ postId }) {
  const [liked, setLiked] = useState(false);
  const [count, setCount] = useState(0);
  
  const handleLike = async () => {
    await fetch(`/api/posts/${postId}/like`, { 
      method: 'POST' 
    });
    setLiked(true);
    setCount(count + 1);
  };
  
  return (
    <button onClick={handleLike}>
      {liked ? '❤️' : '🤍'} {count}
    </button>
  );
}

Migration Strategy

Moving from Client-only to RSC:

  1. Start with Server Components: Default to server
  2. Add 'use client' only when needed: Interactivity, hooks, browser APIs
  3. Push client boundaries down: Keep as much on server as possible
  4. Use composition: Server components can have client children

Performance Benefits

Real-world improvements:

  • Smaller bundle sizes: Up to 60% reduction
  • Faster initial load: Less JavaScript to parse
  • Better SEO: Content rendered on server
  • Improved streaming: Progressive rendering

Common Pitfalls

1. Using Server-only Code in Client Components

'use client';
import fs from 'fs'; // Error: Node.js APIs not availableRemove 'use client' or move to server component

2. Not Using 'use client' When Needed

function Component() {
  const [state, setState] = useState(0); // Error
}

✅ 'use client';
function Component() {
  const [state, setState] = useState(0);
}

3. Over-using Client Components

'use client'; // At top of every fileOnly mark interactive components

Conclusion

React Server Components are not a replacement for client components—they're a complement. Use server components for:

  • Data fetching
  • Heavy computations
  • Direct database access
  • Reducing bundle size

Use client components for:

  • Interactivity
  • Browser APIs
  • State management
  • Event handlers

The future of React is a mix of both, using each where it makes the most sense.

Comments (0)

Loading comments...