NelsonLabs
Next.js Complete Guide/Server vs Client Components

Server vs Client Components

This is the most important concept in modern Next.js — and the one most beginners find confusing. Every component you build runs in one of two places: on the server, or in the browser. By default, everything is a Server Component.

ANALOGY

Real-world analogy: A restaurant kitchen vs dining room. Server Components are like the kitchen — the customer never sees it, but it's where all the real work happens (fetching data, processing, database queries). Client Components are like the dining room — everything the customer interacts with directly (buttons, forms, animations). Both are essential. They serve different purposes.

FeatureServer ComponentClient Component
Default?✅ Yes — no special syntax needed❌ Must add 'use client' at top of file
Runs where?On the server (Node.js)In the user's browser
Can fetch data directly?✅ Yes — async/await works perfectly⚠️ Only via useEffect or libraries
Can access databases?✅ Yes — safely, never sent to user❌ Never — would expose credentials
Can use useState / hooks?❌ No✅ Yes
Can handle click events?❌ No✅ Yes
Good for SEO?✅ Yes — pre-rendered HTML⚠️ Partial — initial load may be empty

Server Component — runs on the server

app/courses/page.jsx — fetches data, renders HTML on the server
jsx
// No "use client" at the top = this is a Server Component
// It runs on the server — this code never reaches the user's browser

export default async function CoursesPage() {
  // You can use async/await directly — this runs on the server
  const response = await fetch("https://api.example.com/courses");
  const courses = await response.json();

  // The HTML is generated on the server and sent to the browser already rendered
  return (
    <ul>
      {courses.map(course => (
        <li key={course.id}>{course.title}</li>
      ))}
    </ul>
  );
}

// This is fast, secure, and great for SEO.
// The user gets fully rendered HTML — not an empty page that loads data later.

Client Component — runs in the browser

components/LikeButton.jsx — interactive, runs in the browser
jsx
"use client";  // ← This one line makes it a Client Component

import { useState } from "react";

export default function LikeButton() {
  // useState holds a value that, when changed, causes the component to re-render
  const [likes, setLikes] = useState(0);
  const [liked, setLiked] = useState(false);

  function handleClick() {
    if (!liked) {
      setLikes(likes + 1);
      setLiked(true);
    }
  }

  return (
    <button onClick={handleClick}
      style={{ color: liked ? "red" : "gray" }}>
      ♥ {likes} {liked ? "Liked!" : "Like"}
    </button>
  );
}

TIP

The simple rule to remember. Ask yourself: does this component need to respond to user clicks, keyboard input, or browser APIs? If yes → add 'use client'. If it just displays data or fetches from a database → leave it as a Server Component. Default to Server Components and only add 'use client' when you actually need interactivity.

Combining them

A Server Component containing a Client Component — this is normal
jsx
// app/course/[slug]/page.jsx  (Server Component — no directive)
import CourseInfo from "@/components/CourseInfo";   // Server Component
import EnrollButton from "@/components/EnrollButton"; // Client Component

export default async function CoursePage({ params }) {
  const { slug } = await params;
  // This fetch runs on the server — secure and fast
  const course = await fetch(`/api/courses/${slug}`).then(r => r.json());

  return (
    <div>
      <CourseInfo course={course} />   {/* Rendered on server */}
      <EnrollButton courseId={course.id} />  {/* Interactive, runs in browser */}
    </div>
  );
}