Published Mar 19, 2026

Understanding React Server Components in Next.js apps

React Server Components represent a fundamental shift in how we build React applications. They run exclusively on the server, which means they can directly access databases, read files, and fetch data — all without sending any JavaScript to the browser.

Server vs Client Components

In Next.js App Router, every component is a Server Component by default. To make a component run on the client, you add the 'use client' directive at the top of the file.

Server Components can:

  • Fetch data directly (no API routes needed)
  • Access backend resources
  • Keep sensitive logic server-side
  • Reduce client bundle size

Client Components are needed for:

  • Interactivity (onClick, onChange, etc.)
  • State management (useState, useReducer)
  • Browser APIs (localStorage, window)
  • Effects (useEffect, useLayoutEffect)

The mental model

Think of it this way: Server Components are your data layer, Client Components are your interaction layer. The boundary between them is the 'use client' directive.

// app/posts/page.tsx — Server Component
import { getPosts } from '@/lib/posts';
import { PostList } from './post-list';

export default async function PostsPage() {
  const posts = await getPosts();
  return <PostList posts={posts} />;
}
// app/posts/post-list.tsx — Client Component
'use client';

import { useState } from 'react';

export function PostList({ posts }) {
  const [search, setSearch] = useState('');
  const filtered = posts.filter(p =>
    p.title.toLowerCase().includes(search.toLowerCase())
  );

  return (
    <div>
      <input value={search} onChange={e => setSearch(e.target.value)} />
      {filtered.map(post => <article key={post.slug}>{post.title}</article>)}
    </div>
  );
}

Common mistakes

Moving the 'use client' boundary too high. If you put 'use client' on a layout or page, everything below it becomes a Client Component. Push the boundary as low as possible — wrap only the interactive parts.

Trying to pass functions as props from Server to Client Components. Functions are not serializable, so you cannot pass callbacks from a Server Component to a Client Component. Instead, use Server Actions.

Fetching data in Client Components when you do not need to. If the data does not change based on user interaction, fetch it in a Server Component and pass it down as props.

Server Components are not a replacement for Client Components — they are complementary. The goal is to use each where it makes the most sense.