What Are React Server Components? The Next.js Default That Confuses Everyone

Why your AI-generated Next.js code breaks, what "use client" actually does, and the simple mental model that makes it click.

TL;DR

In Next.js, every component is a Server Component by default — it runs on the server before reaching the browser. If your component needs interactivity (buttons, forms, useState), you must add "use client" at the top of the file to make it a Client Component. Most AI-generated Next.js errors come from mixing these up. Server = prefab walls built in a factory. Client = custom work done on-site.

Why AI Coders Need to Understand This

Here's a stat that explains a lot: Next.js is the most popular React framework, and it's the one AI tools reach for first. When you ask Claude, ChatGPT, or Cursor to "build me a web app," there's a very high chance it starts with Next.js. And the moment it does, you're dealing with Server Components — whether you know it or not.

The problem? Server Components are the single biggest source of confusion for new Next.js developers. Posts like "Why doesn't useState work?" and "onClick does nothing" flood Reddit's r/nextjs daily. If you're building with AI and don't understand this one concept, you'll spend hours chasing errors that have a two-second fix.

You don't need to understand the engineering behind Server Components. You need to know what they do, when to use each type, and how to fix the errors when AI gets it wrong. That's exactly what this article covers.

The Real Scenario: "Build Me a Dashboard"

Let's say you open Claude or Cursor and type this prompt:

Your Prompt

"Build a Next.js dashboard with a sidebar navigation, a stats overview that shows data from my API, and a chart that updates when I click different time ranges."

Great prompt. AI loves this. It generates a beautiful dashboard — files everywhere, components organized nicely. You run npm run dev, open the browser, and...

⚠️ Error
Error: useState only works in Client Components. Add the "use client" directive at the top of the file to use it.

Or maybe it's worse — no error at all, but your buttons don't do anything. You click "Last 7 days" on your chart filter and... nothing. The chart just sits there. No error in the console. Just a dead button.

This is the Server Component trap, and nearly every AI coder falls into it at least once. Here's what's actually happening.

What AI Generated: Server vs. Client Components Side by Side

When AI generated your dashboard, it created two types of components. Understanding the difference is the whole game.

A Server Component (the default)

This is what a Server Component looks like. Notice: no special directive at the top. In Next.js, if there's no "use client", it's automatically a Server Component.

// app/dashboard/page.jsx
// No "use client" = Server Component (this is the default)

async function DashboardPage() {
  // This runs on the SERVER — the user never sees this code
  const response = await fetch('https://api.example.com/stats');
  const stats = await response.json();

  return (
    <div>
      <h1>Dashboard</h1>
      <p>Total Users: {stats.totalUsers}</p>
      <p>Revenue: ${stats.revenue}</p>
      <ChartWithFilters />
    </div>
  );
}

What this does: This component runs on the server before the page loads. It grabs data from your API, builds the HTML, and sends the finished result to the browser. The user's browser never sees the fetch call or the API URL. It's like getting a prefab wall delivered to your job site — all the work happened at the factory. You just install the finished piece.

A Client Component

Now here's a Client Component. Notice the very first line:

"use client";
// ^^^ This line changes EVERYTHING

import { useState } from "react";

function ChartWithFilters() {
  const [timeRange, setTimeRange] = useState("7d");

  return (
    <div>
      <div className="filters">
        <button onClick={() => setTimeRange("7d")}>7 Days</button>
        <button onClick={() => setTimeRange("30d")}>30 Days</button>
        <button onClick={() => setTimeRange("90d")}>90 Days</button>
      </div>
      <Chart data={timeRange} />
    </div>
  );
}

What this does: This component runs in the user's browser. It has buttons. It tracks which time range is selected. It responds to clicks. This is the custom work that has to happen on-site — you can't pre-build a light switch in a factory and expect it to work without wiring it into the house.

🏗️ The Construction Analogy

Server Components = Prefab walls built in the factory. Done before they arrive at the site. Cheaper, faster, but you can't change them once they're installed.

Client Components = Custom work done on-site. Light switches, outlets, thermostats — anything the homeowner interacts with. More expensive, but necessary for interactivity.

A good house uses both. So does a good Next.js app.

Understanding Each Part

What "use client" Actually Is

"use client" is not importing a library. It's not a React feature you install. It's a directive — a single line at the top of a file that tells Next.js: "This component needs to run in the browser."

"use client";  // Must be the VERY FIRST LINE — before any imports

import { useState } from "react";
// ... rest of your component

Rules for "use client":

  • It goes on the very first line of the file — before imports, before comments, before anything.
  • It affects the entire file, not individual components. Every component exported from that file becomes a Client Component.
  • It creates a boundary. Once a file is marked "use client," all components it imports are also treated as client code.

When to Use Each: The Decision Checklist

This is the practical part. Print this out if you need to.

Does Your Component Need... Server Component Client Component
To fetch data from an API or database ✅ Yes — directly with async/await ⚠️ Possible, but less efficient
onClick, onChange, or other event handlers ❌ Not possible ✅ Yes — this is why it exists
useState or useReducer (tracking state) ❌ Not possible ✅ Yes
useEffect (doing something after render) ❌ Not possible ✅ Yes
To display static content (text, images, layout) ✅ Yes — and it's faster ⚠️ Works, but wastes resources
To use browser APIs (localStorage, window) ❌ Not possible — no browser on the server ✅ Yes
To keep API keys or secrets hidden ✅ Yes — code never reaches the browser ❌ Dangerous — secrets exposed to users

The quick rule: If a user interacts with it, it's a Client Component. If it just displays data or fetches from a server, keep it as a Server Component.

The Mental Model: Think in Layers

The best way to think about a Next.js page is in layers:

  1. The outer shell (page layout, header, navigation structure) → Server Component. This never changes based on what the user clicks. Build it in the factory.
  2. The data display (stats, lists, content from your database) → Server Component. Fetch it on the server, send the finished HTML.
  3. The interactive pieces (buttons, dropdowns, modals, forms, filters) → Client Component. These need to respond to user actions in real time.

Think of a building going up. The structural steel, the framing, the drywall — that's all prefab (server). The electrical, the plumbing fixtures, the HVAC controls — that's all installed on-site (client) because someone has to interact with it.

Here's what this looks like in code:

// app/dashboard/page.jsx — SERVER Component (the shell)

import { ChartWithFilters } from "./ChartWithFilters";
import { Sidebar } from "./Sidebar";

async function DashboardPage() {
  // Fetch on the server — fast, secure, no loading spinners
  const stats = await fetch('https://api.example.com/stats')
    .then(res => res.json());

  return (
    <div className="dashboard">
      <Sidebar />                          {/* Server: just layout */}
      <main>
        <h1>Dashboard</h1>
        <StatsDisplay stats={stats} />     {/* Server: just shows data */}
        <ChartWithFilters />               {/* CLIENT: has buttons! */}
      </main>
    </div>
  );
}

Notice how ChartWithFilters is the only Client Component here. Everything else stays on the server. That's the ideal pattern. Server by default, client only when necessary.

What AI Gets Wrong (And How to Fix It)

AI is genuinely great at writing Next.js code. But it makes three specific Server Component mistakes constantly. Learn to spot these and you'll save yourself hours of debugging.

Mistake #1: Missing "use client" When It's Needed

This is the most common one. AI generates a component with useState or onClick but forgets the "use client" directive.

❌ What AI Generated
// components/SearchBar.jsx
import { useState } from "react";

export function SearchBar() {
  const [query, setQuery] = useState("");

  return (
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Search..."
    />
  );
}
✅ The Fix
"use client";

// components/SearchBar.jsx
import { useState } from "react";

export function SearchBar() {
  const [query, setQuery] = useState("");

  return (
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Search..."
    />
  );
}

How to spot it: You see useState, useEffect, useRef, or any on___ event handler — but no "use client" at the top. Add it.

Mistake #2: Putting "use client" on Everything

The opposite problem. AI puts "use client" on every single file "just to be safe." This works — your app runs — but you lose all the benefits of Server Components.

"use client";  // ← Why is this here?

// This component just displays a list. No state. No clicks.
// It should be a Server Component!

export function UserList({ users }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

The problem: When everything is a Client Component, your app ships more JavaScript to the browser, loads slower, and can't fetch data as efficiently. It's like doing all your construction work on-site when half of it could've been done cheaper and faster at the factory.

The fix: Ask AI: "Which of these components actually need interactivity? Remove 'use client' from any component that doesn't use hooks or event handlers."

Mistake #3: Trying to Fetch Data in a Client Component

AI sometimes generates a Client Component that tries to fetch data using the Server Component pattern:

"use client";

// ❌ This pattern doesn't work right in a Client Component
export async function UserProfile() {
  const user = await fetch('/api/user').then(r => r.json());
  return <div>{user.name}</div>;
}

Client Components can't be async functions. If a Client Component needs data, it has to use useEffect and useState to fetch it after the page loads — or better yet, get the data passed down from a Server Component parent as props.

// ✅ Option 1: Fetch in a Server Component, pass data down
// app/profile/page.jsx (Server Component — no "use client")
import { ProfileCard } from "./ProfileCard";

export default async function ProfilePage() {
  const user = await fetch('/api/user').then(r => r.json());
  return <ProfileCard user={user} />;
}

// ProfileCard.jsx (Client Component — has interactivity)
"use client";
export function ProfileCard({ user }) {
  return <div onClick={() => alert(user.name)}>{user.name}</div>;
}

This pattern — Server Component fetches, Client Component interacts — is the golden rule of Next.js architecture. Tell your AI this explicitly and it'll generate much better code.

How to Debug Server Component Errors

When something breaks in a Next.js app and you suspect it's a Server/Client Component issue, here's your debugging workflow:

Step 1: Read the Error Message

Next.js gives remarkably clear error messages for this specific problem. Look for these phrases:

  • "useState only works in Client Components" → Add "use client" to the file
  • "Event handlers cannot be passed to Client Component props" → The parent is a Server Component trying to pass onClick to a child. Mark the child (or a wrapper) as "use client"
  • "async/await is not yet supported in Client Components" → Remove "use client" or move the data fetching to a Server Component parent

Step 2: Check the First Line

Open the file that's causing the error. Look at line 1. Is "use client" there? Should it be? Use the decision checklist from earlier.

Step 3: Ask AI the Right Question

Don't just paste the error. Give AI context about the component architecture:

Better Debugging Prompt

"This component uses useState and onClick handlers but it's failing. I think it's a Server Component / Client Component issue. Here's the file — does it need 'use client'? And should any of its parent components change?"

Step 4: Trace the Component Tree

Sometimes the error isn't in the file that broke — it's in a parent or child. Remember:

  • A Server Component can import a Client Component ✅
  • A Client Component cannot import a Server Component ❌
  • The "use client" boundary flows downward — once a file is marked client, everything it imports is also client

Think of it like the building code inspector showing up. If the main electrical panel (parent) is wired wrong, every outlet downstream has problems. Fix the source, and the downstream components work again.

Step 5: When in Doubt, Split the File

If a component needs both server data and client interactivity, split it into two files:

// 1. Server Component: fetches the data
// app/products/page.jsx
import { ProductFilters } from "./ProductFilters";

export default async function ProductsPage() {
  const products = await fetch('/api/products').then(r => r.json());
  return (
    <div>
      <h1>Products</h1>
      <ProductFilters products={products} />
    </div>
  );
}

// 2. Client Component: handles the filtering
// app/products/ProductFilters.jsx
"use client";
import { useState } from "react";

export function ProductFilters({ products }) {
  const [filter, setFilter] = useState("all");
  const filtered = products.filter(p =>
    filter === "all" ? true : p.category === filter
  );

  return (
    <div>
      <select onChange={(e) => setFilter(e.target.value)}>
        <option value="all">All</option>
        <option value="electronics">Electronics</option>
        <option value="clothing">Clothing</option>
      </select>
      <ul>
        {filtered.map(p => <li key={p.id}>{p.name}</li>)}
      </ul>
    </div>
  );
}

This "split and pass" pattern solves 90% of Server Component headaches. The server does the heavy lifting (data fetching), and the client handles the interactive parts (filtering). Factory work and on-site work, each where they belong.

What to Tell Your AI (Prompts That Prevent These Errors)

The best debugging is preventing bugs in the first place. Add these instructions to your prompts when building Next.js apps:

Prompt Template for Next.js

"Use Next.js App Router. Keep components as Server Components by default. Only add 'use client' to components that need useState, useEffect, or event handlers. Fetch data in Server Components and pass it as props to Client Components."

That one paragraph, added to the beginning of your prompt, prevents most Server Component mistakes before they happen. AI tools are great at following architectural instructions — you just have to give them.

What to Learn Next

Now that you understand Server Components vs. Client Components, here's your learning path:

  • What Is React? — If the React basics are still fuzzy, start here. Server Components are a React feature, so understanding React's component model helps everything click.
  • What Is Next.js? — The full picture of what Next.js adds on top of React, including routing, layouts, and the App Router that made Server Components the default.
  • What Is the Fetch API? — Server Components use fetch() to get data. Understanding how fetch works makes Server Component data loading much clearer.
  • What Is an API? — Server Components often talk to APIs. If you're not sure what an API actually is or does, this fills that gap.
  • How to Debug AI-Generated Code — A complete framework for tracking down bugs in code your AI wrote, including Server Component issues.

Quick Reference Card

Server Component (Default)

  • No directive needed — it's the default
  • Can use async/await directly
  • Can access databases and APIs securely
  • Cannot use useState, useEffect, useRef
  • Cannot use onClick, onChange, or any event handler
  • Cannot use browser APIs (window, localStorage)
  • Sends finished HTML to the browser — fast

Client Component ("use client")

  • Requires "use client" on the first line
  • Can use all React hooks
  • Can use event handlers and browser APIs
  • Cannot be async — use useEffect for data
  • Ships JavaScript to the browser — heavier
  • Best for: buttons, forms, modals, dropdowns, filters

The Golden Rule

Fetch data in Server Components. Handle interactivity in Client Components. Pass data between them with props.

Frequently Asked Questions

What is the difference between Server Components and Client Components in Next.js?

Server Components run on the server before the page reaches the browser. They can fetch data directly, access databases, and keep sensitive logic hidden — but they cannot use interactive features like useState, onClick, or useEffect. Client Components run in the browser and handle all interactivity — buttons, forms, animations, anything the user clicks or types. In Next.js, every component is a Server Component by default unless you add "use client" at the top of the file.

Why does useState not work in my Next.js component?

useState is a React hook that only works in Client Components. In Next.js, every component is a Server Component by default, and Server Components cannot use hooks like useState, useEffect, or useRef. To fix this, add the directive "use client" as the very first line of the file — before any imports. This tells Next.js to treat the component as a Client Component.

What does "use client" mean in Next.js?

"use client" is a directive you place at the very top of a React component file. It tells Next.js: "This component needs to run in the browser." Without it, Next.js assumes the component is a Server Component and will throw errors if you try to use interactive features like event handlers, state, or browser APIs. It's not importing a library — it's a boundary marker that switches how Next.js processes that file.

Can I use Server Components and Client Components together?

Yes — and you should. The best pattern is to keep your page-level components as Server Components (for fast loading and data fetching) and wrap only the interactive parts in Client Components. Think of it like building a house: the structural framing is done in the factory (server), but the light switches and thermostats are installed on-site (client). You can import Client Components into Server Components, but not the other way around.

Why does AI keep adding "use client" to every file?

AI tools like Claude and ChatGPT often add "use client" as a safety net because it prevents the most common errors — if the component uses any interactivity at all, it needs to be a Client Component. The downside is that making everything a Client Component defeats the purpose of Server Components: faster loads, smaller bundles, and server-side data access. The fix is to only add "use client" to files that actually need interactivity (useState, onClick, useEffect, etc.).