Technology · Next.js

Next.js Directives

Use use server and use client directives to control rendering and data access.

TL;DR
  1. 01"use client" marks components to render on the client.
  2. 02"use server" marks functions to run only on the server.
  3. 03Server components are the default in the App Router.

Server Components (Default)

  • Server components render on the server only.
    // app/page.tsx
    export default async function Home() {
      const res = await fetch("https://api.example.com/data");
      const data = await res.json();
      return <div>{data.title}</div>;
    }
  • Server components can fetch data and access secrets.
  • No JavaScript sent to the browser for these components.
  • Can use async/await directly in the component.
  • Access environment variables and databases without exposing them.
    export default async function AdminPage() {
      const users = await db.user.findMany();
      return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
    }
  • Server components cannot use event handlers or browser APIs.
    // This would cause an error in a server component:
    // const [count, setCount] = useState(0); // ❌ hooks not allowed

Client Components

  • Mark components to render on the client with "use client".
    "use client";
    
    import { useState } from "react";
    
    export default function Counter() {
      const [count, setCount] = useState(0);
      return <button onClick={() => setCount(count + 1)}>{count}</button>;
    }
  • Must use "use client" at the top of the file.
  • Can use hooks like useState, useEffect, useContext.
  • Cannot access server-only resources.
  • Use for interactive features like forms, modals, and tabs.
    "use client";
    
    export function Tabs({ items }) {
      const [active, setActive] = useState(0);
      return (
        <div>
          {items.map((item, i) => (
            <button key={i} onClick={() => setActive(i)}>{item.label}</button>
          ))}
          <div>{items[active].content}</div>
        </div>
      );
    }
  • The directive affects the file and all its imports.
    "use client";
    // All imports here become part of the client bundle
    import { Chart } from "./Chart"; // also becomes client-side

Server Functions

  • Mark functions to run only on the server with "use server".
    "use server";
    
    export async function submitForm(formData: FormData) {
      const email = formData.get("email");
      // Save to database securely
      return { success: true };
    }
  • Server functions can be called from client components.
    "use client";
    
    async function handleSubmit(formData: FormData) {
      const result = await submitForm(formData);
    }
  • Functions run securely on the server.
  • Use server functions with HTML form actions for progressive enhancement.
    // app/form.tsx (server component)
    import { saveContact } from "./actions";
    
    export default function ContactForm() {
      return <form action={saveContact}><input name="email" /><button>Send</button></form>;
    }
  • Validate input inside server functions before writing to the database.
    "use server";
    
    export async function createPost(formData: FormData) {
      const title = formData.get("title") as string;
      if (!title || title.length < 3) return { error: "Title too short" };
      await db.post.create({ data: { title } });
      return { ok: true };
    }

Mixing Server and Client

  • Pass server components as children to client components.
    "use client";
    
    export default function Layout({ children }) {
      return <div>{children}</div>;
    }
    
    // In a server component:
    <Layout><ServerSidebar /></Layout>
  • Fetch data in a server component and pass it as a prop.
    // Server component
    export default async function Page() {
      const data = await getData();
      return <ClientChart data={data} />;
    }
  • Client components can import server components as children.
  • Use server components as high-level wrappers to keep fetching centralized.
    export default async function BlogLayout({ children }) {
      const categories = await getCategories();
      return (
        <div>
          <ServerNav categories={categories} />
          {children}
        </div>
      );
    }
  • Keep the "use client" boundary as low in the tree as possible.
    // Good: only the button is a client component
    export default async function Post() {
      const post = await getPost();
      return <article>{post.body}<LikeButton id={post.id} /></article>;
    }

Best Practices

  • Use server components by default for performance.
    // Good: server component fetches data
    export default async function Posts() {
      const posts = await getPosts();
      return posts.map(post => <Post key={post.id} post={post} />);
    }
  • Mark leaf components as "use client" for interactivity.
    "use client";
    // Only interactive components
    export function Favorite({ postId }) {
      const [liked, setLiked] = useState(false);
      return <button onClick={() => setLiked(!liked)}>{liked ? "Liked" : "Like"}</button>;
    }
  • Use server-only package to prevent accidental client imports.
    import "server-only";
    // This file throws an error if imported in a client bundle
    export async function getSecretData() { ... }
  • Avoid passing non-serializable values like functions as props.
    // Wrong: functions can't cross the server-client boundary as props
    <ClientComp onClick={serverFunction} /> // ❌
    // Use server actions instead
    <ClientComp action={serverAction} />    // ✅
  • Test directive placement by checking the Network tab for unexpected JS.
    # Open browser DevTools > Network > JS
    # Check that server-only components don't appear in client bundles

Tip: Use server components for fetching data and accessing secrets — it's faster and more secure than client components.

Warning: "use client" at the top of a file makes the entire file and its imports client-side — keep client-heavy components in separate files.

Next.js DeploymentNext.js Environment Variables