Skip to content

Server Components

Next.js 13’s server components represent a fundamental shift in how applications are structured, emphasizing separation between UI logic and rendering processes. By leveraging the App Router, server components enable developers to write rendering logic that executes on the server, while client components handle interactivity. This architecture improves performance, SEO, and scalability by decoupling data fetching from UI rendering.


Core Principles of Server Components

Separation of Concerns

Server components are designed to handle rendering logic and data fetching on the server. They cannot contain client-side state or effects, ensuring that UI logic remains isolated from rendering. For example, a server component might fetch user data and pass it to a client component that displays it, ensuring that the UI logic (e.g., formatting the display) is isolated from the data fetching logic. This separation allows developers to focus on rendering static HTML and data pipelines without worrying about client-side hydration.

Server-Side Rendering (SSR) and Static Generation (SSG)

Server components are rendered on the server, producing HTML that is sent to the client. This enables SSR for dynamic content and SSG for static pages, improving SEO and initial load times. The server handles all rendering, while the client only hydrates the UI for interactivity.

Hydration Process

After the server renders HTML, the client browser hydrates the page by attaching event handlers to DOM elements. This ensures that interactivity (e.g., form submissions, animations) works seamlessly without requiring full page reloads.


Architecture Overview

App Router Structure

Server components are organized within the app directory, with each route defining a component tree. For example:

app/
├── page.tsx        # Server component for the root route
├── dashboard/
│   └── page.tsx    # Server component for /dashboard
The App Router enforces a tree structure, ensuring components are rendered in the correct order and context.

Server vs. Client Components

  • Server components (e.g., page.tsx) are rendered on the server and can use fetch for data fetching.
  • Client components (e.g., client-component.tsx) are marked with use client and handle interactivity via hooks like useState or useEffect.

Data Fetching in Server Components

Server components can fetch data using fetch or useServerInsertedHTML for injecting scripts/styles. For example:

// app/page.tsx
export default async function Page() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();
  return <div>{data.message}</div>;
}
This data is then passed to client components for rendering.


Benefits and Use Cases

  • Performance: Server components reduce client-side JavaScript payload, improving load times.
  • SEO: Server-rendered HTML is crawled by search engines, enhancing visibility.
  • Scalability: Modular, server-centric architecture simplifies managing large applications.
  • Security: Sensitive logic (e.g., authentication) can be executed on the server without exposing client-side code.

Example: Server Component with Data Fetching

// app/dashboard/page.tsx
export default async function Dashboard() {
  const res = await fetch('https://api.example.com/stats', { cache: 'force-cache' });
  const stats = await res.json();

  return (
    <div>
      <h1>Dashboard</h1>
      <ClientComponent stats={stats} />
    </div>
  );
}
// app/dashboard/client-component.tsx
'use client';

import { useEffect, useState } from 'react';

export default function ClientComponent({ stats }: { stats: any }) {
  const [count, setCount] = useState(stats.views);

  useEffect(() => {
    const interval = setInterval(() => {
      setCount(prev => prev + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  return <p>Views: {count}</p>;
}

Diagram: Server Component Rendering Flow

[Request] → Server → Render Server Component → HTML + Data → Client → Hydrate → Interactive UI
This flow illustrates how server components generate HTML, which is then hydrated by the client for interactivity.


Key takeaways

  • Server components execute rendering logic on the server, decoupling UI from data fetching.
  • The App Router enforces a structured, tree-based architecture for scalable applications.
  • Hydration ensures interactivity without compromising performance or SEO.
  • Use fetch in server components to optimize data pipelines and reduce client-side overhead.