DEV Community

Atlas Whoff
Atlas Whoff

Posted on

React Server Components: What They Are and When to Use Them

React Server Components: What They Are and When to Use Them

React Server Components (RSC) run exclusively on the server — no hydration, no JavaScript sent to the client. They fundamentally change how you think about data fetching.

Server vs Client Components

Server Component Client Component
Runs on Server only Server + Client
JS bundle Not included Included
Can use hooks No Yes
Can access DB Yes No (use API routes)
Default in Next.js 14 Yes No (needs 'use client')

Server Component (Default)

// This is a Server Component by default in Next.js 14 App Router
// No 'use client' directive needed

async function UserDashboard({ userId }: { userId: string }) {
  // Direct database access — runs only on server
  const user = await prisma.user.findUnique({ where: { id: userId } });
  const posts = await prisma.post.findMany({ where: { userId }, take: 5 });

  // No useEffect, no loading state, no API call needed
  return (
    <div>
      <h1>Welcome, {user?.name}</h1>
      <PostList posts={posts} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Client Component

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

// Only add 'use client' when you need:
// - useState, useEffect, or other hooks
// - Browser APIs (window, localStorage)
// - Event handlers (onClick, onChange)
// - Third-party client-only libraries

export function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
  const [value, setValue] = useState('');

  return (
    <input
      value={value}
      onChange={e => { setValue(e.target.value); onSearch(e.target.value); }}
      placeholder='Search...'
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Composing Server and Client

// Server Component wraps Client Component
async function Page() {
  const initialData = await fetchData(); // Server-side

  return (
    <div>
      <h1>Dashboard</h1>
      {/* Pass server data as props to client component */}
      <InteractiveChart data={initialData} />
      {/* Server component renders alongside client component */}
      <StaticSidebar />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Streaming with Suspense

import { Suspense } from 'react';

async function Page() {
  return (
    <div>
      {/* Fast: renders immediately */}
      <Header />

      {/* Slow query: streams in when ready */}
      <Suspense fallback={<Skeleton />}>
        <SlowDataComponent />
      </Suspense>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Server Actions (Mutations)

// No API route needed — server action runs on server
async function updateName(formData: FormData) {
  'use server';
  const name = formData.get('name') as string;
  await prisma.user.update({ where: { id: session.user.id }, data: { name } });
  revalidatePath('/dashboard');
}

export function NameForm() {
  return (
    <form action={updateName}>
      <input name='name' />
      <button type='submit'>Update</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

When to Use Each

Default to Server Components. Add 'use client' only when you need interactivity, hooks, or browser APIs. The goal: push the client boundary as far down the component tree as possible.

Next.js App Router with Server Components ships pre-configured in the AI SaaS Starter Kit — proper RSC patterns throughout. $99 at whoffagents.com.

Top comments (0)