Skip to content

Server Components

Server Components are the foundation the entire App Router is built on: components that render on the server and send only HTML (plus minimal instructions) to the browser, with zero of their own JavaScript shipped to the client. This lesson explains how and why they work.

What Makes a Component a Server Component?

In the App Router, every component is a Server Component unless its file starts with a "use client" directive. Server Components run exclusively on the server (or at build time), meaning their code — including any imports, like a database client or an API secret key — never gets bundled into the JavaScript sent to the browser.

Because they run on the server, Server Components can be async functions, letting you await a database query or fetch() call directly inside the component body, with the resulting data already baked into the rendered HTML by the time it reaches the browser.

// app/products/page.tsx — a Server Component (no directive needed)
import { db } from "@/lib/db";

export default async function ProductsPage() {
  const products = await db.product.findMany();

  return (
    <ul>
      {products.map((p) => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

The db client and every line of this component's logic run only on the server; the browser receives just the resulting <ul> HTML.

Server Component Rules

// Allowed in a Server Component:
async function Component() { /* await db calls, fetch, etc. */ }

// NOT allowed in a Server Component:
// useState, useEffect, onClick, or any browser-only API
  • No directive at the top of the file means a component is a Server Component (the default).
  • Server Components can be async and can directly access server-only resources.
  • Server Components cannot use React hooks like useState/useEffect, or event handlers like onClick.
  • Server Components can render Client Components, passing them serializable data as props.

Server Components Cheat Sheet

What Server Components can and can't do.

Capability Server Component
Can be async Yes
Can use useState / useEffect No
Can use event handlers (onClick) No
Ships JavaScript to the browser No (its own code, at least)
Can access secrets/env vars safely Yes
Can query a database directly Yes
Can render Client Components Yes, as children

Server Components vs. Traditional SSR

Traditional server-side rendering (like the Pages Router's getServerSideProps) still ships the entire page's component code as JavaScript to the browser for hydration, even though the initial HTML was rendered on the server. Server Components go further: their code never ships to the browser at all, because they never need to run there again — there's no hydration step for pure Server Component output.

Aspect Traditional SSR Server Components
Initial HTML from the server Yes Yes
Component JS shipped to browser Yes (for hydration) No
Re-renders in the browser Yes Never (unless it's a Client Component)

Composing Server and Client Components

A Server Component can import and render a Client Component directly. The reverse — a Client Component importing a Server Component directly — is not allowed, though a Client Component can still receive a pre-rendered Server Component as a children prop, which is the recommended pattern.

  • Server Component → renders → Client Component: always allowed.
  • Client Component → imports → Server Component: not allowed directly.
  • Client Component → receives a Server Component via children/props: allowed and common.

Common Mistakes

  • Adding "use client" to a component that only fetches and displays data, unnecessarily shipping it to the browser.
  • Trying to import a Server Component directly inside a Client Component's file.
  • Passing non-serializable values (like a function or a class instance) from a Server Component to a Client Component as props.
  • Assuming Server Components re-render in the browser on state changes elsewhere in the app — they don't re-run client-side at all.

Key Takeaways

  • Server Components are the App Router default; no directive is needed to opt in.
  • They run only on the server and can be async, enabling direct data fetching.
  • They cannot use hooks or event handlers, since they never execute in the browser.
  • Their own code is never shipped to the browser, reducing client-side JavaScript.
  • Server Components can render Client Components, but not vice versa (directly).

Pro Tip

When designing a new feature, sketch which parts genuinely need interactivity first, and default everything else to Server Components — most real-world UI (data display, formatting, layout) needs no client-side JavaScript at all.