DEV Community

Cover image for Server and Client Component Patterns in Next.js
Abdullah
Abdullah

Posted on

Server and Client Component Patterns in Next.js

Table of Contents

When to Use Each Type

Server Components Can:

  • Fetch data directly
  • Access backend resources (databases, internal APIs)
  • Keep sensitive information secure (tokens, API keys)
  • Hold large dependencies that don't need to be sent to the client

Client Components Can:

  • Add interactivity with event listeners (onClick, onChange)
  • Use React state and lifecycle effects (useState, useEffect)
  • Access browser-only APIs (window, localStorage)
  • Use custom hooks that depend on state or browser APIs
  • Use React class components

Client Components need the "use client" directive at the top of their file to tell Next.js they should run on the client.

Server Component Patterns

Sharing Data Between Components

When multiple server components need the same data, you don't need to pass props down or use context. Instead, each component can fetch the data it needs directly.

Next.js automatically memoizes fetch calls, so if multiple components request the same data, the actual fetch happens only once. For non-fetch functions, use React's cache function for the same effect.

Keeping Server Code Out of the Client

Sometimes server-specific code might accidentally be included in client bundles, like code accessing private environment variables. To prevent this, use the server-only package:

// In your server-only module
import "server-only";

export async function getData() {
  // Code that should only run on the server
  const res = await fetch("https://api.example.com", {
    headers: { authorization: process.env.API_KEY },
  });
  return res.json();
}
Enter fullscreen mode Exit fullscreen mode

If any client component tries to import this module, you'll get a build-time error. For client-only code, use the client-only package.

Using Third-Party Components

Many third-party components haven't yet added the "use client" directive despite using client-only features. These will work in Client Components but fail in Server Components.

The solution is to wrap these third-party components in your own Client Component:

// carousel.tsx
"use client";
import { Carousel } from "acme-carousel";
export default Carousel;
Enter fullscreen mode Exit fullscreen mode

Now you can use your wrapped component in Server Components.

Using Context Providers

React context doesn't work in Server Components. To use context, create your provider in a Client Component:

// theme-provider.tsx
"use client";
import { createContext } from "react";
export const ThemeContext = createContext({});

export default function ThemeProvider({ children }) {
  return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>;
}
Enter fullscreen mode Exit fullscreen mode

Then use it in your Server Component layout:

// layout.tsx
import ThemeProvider from "./theme-provider";

export default function Layout({ children }) {
  return (
    <html>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Place providers as deep in the tree as possible to help Next.js optimize static parts.

Client Component Patterns

Moving Client Components Down the Tree

To reduce JavaScript sent to the browser, move interactive parts into separate Client Components rather than making entire layouts or pages client components.

Example: Keep a layout as a Server Component, but make just the interactive search bar a Client Component.

Passing Props from Server to Client Components

You can pass data from Server to Client Components, but the props must be serializable (can be converted to JSON). Complex objects like functions, dates, or classes won't work.

If you need non-serializable data, either fetch it directly in the Client Component or use a Route Handler.

Interleaving Server and Client Components

You can think of your UI as a tree starting with the root layout (a Server Component). Some subtrees can be rendered on the client by adding "use client".

Within client subtrees, you can still include Server Components, but there are important rules to follow.

Pattern to Avoid: Importing Server Components into Client Components

This doesn't work:

"use client";
// ❌ Error: You cannot import a Server Component into a Client Component
import ServerComponent from "./server-component";

export default function ClientComponent() {
  const [count, setCount] = useState(0);
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <ServerComponent />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Supported Pattern: Passing Server Components as Props

This works well:

// In client-component.tsx
"use client";
export default function ClientComponent({ children }) {
  const [count, setCount] = useState(0);
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      {children}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode
// In page.tsx (a Server Component)
import ClientComponent from "./client-component";
import ServerComponent from "./server-component";

export default function Page() {
  return (
    <ClientComponent>
      <ServerComponent />
    </ClientComponent>
  );
}
Enter fullscreen mode Exit fullscreen mode

This approach allows both components to render independently → the Server Component renders on the server before the Client Component renders on the client.

You're not limited to just the children prop. You can use any prop to pass Server Component content to a Client Component.

tutorial image

Next.js Tutorial 2025 - Build a Full Stack Social App

In this 4-hour hands-on tutorial, Codesistency walks you through the process of building a social platform from scratch with Next.js (App Router), React, Prisma ORM, Clerk for authentication, Neon for PostgreSQL hosting, Tailwind CSS, Shadcn UI, and UploadThing for image uploads.

Watch the video →

Top comments (0)

Neon image

Next.js applications: Set up a Neon project in seconds

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Get started →

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay