DEV Community

Alex Spinov
Alex Spinov

Posted on

React Server Components Have Changed React Forever — Here's the Complete Mental Model

React Server Components are not SSR. They are a fundamentally new way to build React apps where components run on the server and send zero JavaScript to the client.

The Key Insight

Server Components run on the server and never re-render on the client. They send their rendered output (not JavaScript) to the browser.

Server Component: Runs on server → Sends HTML/RSC payload → Zero JS on client
Client Component: Runs on both → Sends JavaScript → Hydrates on client
Enter fullscreen mode Exit fullscreen mode

Server Components (Default in Next.js App Router)

// app/posts/page.tsx (Server Component by default)
import { db } from '@/lib/db';

export default async function PostsPage() {
  // Direct database access! No API needed.
  const posts = await db.posts.findMany({
    include: { author: true },
    orderBy: { createdAt: 'desc' },
  });

  return (
    <div>
      <h1>Posts</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>By {post.author.name}</p>
          <p>{post.content}</p>
        </article>
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

This component:

  • Queries the database directly (no API route!)
  • Sends zero JavaScript to the browser
  • Cannot use useState, useEffect, or event handlers

Client Components

// components/LikeButton.tsx
'use client'; // This makes it a Client Component

import { useState } from 'react';

export function LikeButton({ postId, initialLikes }) {
  const [likes, setLikes] = useState(initialLikes);
  const [liked, setLiked] = useState(false);

  const handleLike = async () => {
    setLiked(!liked);
    setLikes(l => liked ? l - 1 : l + 1);
    await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
  };

  return (
    <button onClick={handleLike}>
      {liked ? 'Unlike' : 'Like'} ({likes})
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Mixing Server and Client

// app/posts/[id]/page.tsx (Server Component)
import { LikeButton } from '@/components/LikeButton';
import { CommentSection } from '@/components/CommentSection';

export default async function PostPage({ params }) {
  const post = await db.posts.findUnique({
    where: { id: params.id },
    include: { author: true },
  });

  return (
    <article>
      {/* Server-rendered, zero JS */}
      <h1>{post.title}</h1>
      <p>By {post.author.name}</p>
      <div dangerouslySetInnerHTML={{ __html: post.htmlContent }} />

      {/* Client Components — only THESE send JavaScript */}
      <LikeButton postId={post.id} initialLikes={post.likes} />
      <CommentSection postId={post.id} />
    </article>
  );
}
Enter fullscreen mode Exit fullscreen mode

Server Actions

// app/posts/new/page.tsx
export default function NewPostPage() {
  async function createPost(formData: FormData) {
    'use server'; // This runs on the server!

    const title = formData.get('title') as string;
    const content = formData.get('content') as string;

    await db.posts.create({
      data: { title, content, authorId: getCurrentUserId() },
    });

    redirect('/posts');
  }

  return (
    <form action={createPost}>
      <input name="title" placeholder="Title" required />
      <textarea name="content" placeholder="Content" required />
      <button type="submit">Publish</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

When to Use Which

Need Use
Fetch data Server Component
Read files, env vars Server Component
Use database directly Server Component
Use useState/useEffect Client Component
Event handlers (onClick) Client Component
Browser APIs Client Component
Forms with JS validation Client Component
Static content Server Component

Bundle Size Impact

Traditional React App:      250KB JavaScript
With Server Components:     ~50KB JavaScript (only interactive parts)
Enter fullscreen mode Exit fullscreen mode

Building data-rich applications? Check out my Apify actors — extract data from any website for your server components. For custom solutions, email spinov001@gmail.com.

Top comments (0)