Dynavo Labs
HomeAbout Us
Service
Case StudiesInsightsContact
Get Quote
Dynavo Labs

We design and build digital products that grow businesses — websites, mobile apps, and everything in between.

Services

  • Software Development
  • Web Development
  • Mobile Development
  • AI Development
  • Quality Assurance
  • DevOps & Cloud

Company

  • About
  • Industries
  • Technology
  • Contact

Resources

  • Insights
  • Case Studies

© 2026 dynavo Labs. All rights reserved.

Privacy PolicyTerms & Conditions
dynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labs

Server Components vs Client Components in Next.js

What React Server Components and Client Components are in Next.js, what each can do, how to combine them, and a checklist for where to put 'use client'.

By Muzammil•October 31, 2026•7 min read•Software Development•Frontend
Server Components vs Client Components in Next.js

In the Next.js App Router, every component is a Server Component unless you say otherwise. That one default changes how you structure a page, and it is the source of most confusion about the two kinds of component.

This guide explains what each one does, what each can and cannot do, how to combine them, and how to decide where the 'use client' line goes.

What Is a Server Component?

A Server Component runs only on the server. It can read from a database, call internal services and use secrets, and it sends the rendered result to the browser. Its code is not included in the JavaScript bundle the browser downloads.

// app/products/page.tsx — a Server Component by default
import { listProducts } from '@/lib/products'

export default async function ProductsPage() {
  const products = await listProducts()

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

The component is async and fetches data directly. There is no useEffect, no loading state wired by hand, and no API route sitting between the page and the data.

What Is a Client Component?

A Client Component is marked with the 'use client' directive at the top of the file. It is rendered to HTML on the server for the first load, then hydrated in the browser, where its JavaScript runs and makes it interactive.

'use client'

import { useState } from 'react'

export function LikeButton({ initialCount }: { initialCount: number }) {
  const [count, setCount] = useState(initialCount)

  return <button onClick={() => setCount(count + 1)}>Likes: {count}</button>
}

The name can mislead. Client Components are still rendered on the server for the first response. The difference is that their code also ships to the browser and runs there.

What Each One Can Do

CapabilityServer ComponentClient Component
Fetch data with async/await in the componentYesNo
Access a database or secrets directlyYesNo
Use useState, useEffect, useReducerNoYes
Handle events such as onClickNoYes
Use browser APIs such as window or localStorageNoYes
Adds to the client JavaScript bundleNoYes
Can render the other kindYes, as children or propsOnly as children or props passed from a server parent

The Rule for Choosing

Start with a Server Component. Add 'use client' only when a component needs one of these:

  • State or effects (useState, useEffect)
  • Event handlers (onClick, onChange, onSubmit)
  • Browser-only APIs (window, localStorage, IntersectionObserver)
  • A library that relies on any of the above, such as most chart and animation libraries

If none of those apply, keep it on the server and the browser downloads less code.

Push the Client Boundary Down

'use client' applies to the file and everything that file imports. Placing it on a page or a layout pulls the whole subtree into the client bundle.

Instead of making the whole page a client component for one button:

// app/products/[id]/page.tsx — stays a Server Component
import { getProduct } from '@/lib/products'
import { AddToCartButton } from './add-to-cart-button'

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const product = await getProduct(id)

  return (
    <article>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <AddToCartButton productId={product.id} />
    </article>
  )
}
// app/products/[id]/add-to-cart-button.tsx — the only client code
'use client'

export function AddToCartButton({ productId }: { productId: string }) {
  return <button onClick={() => addToCart(productId)}>Add to cart</button>
}

Only the button's code reaches the browser. The heading, the description and the data access stay on the server.

Combining the Two

A Server Component can import and render a Client Component. The reverse needs care. A Client Component cannot import a Server Component, because everything it imports becomes client code. It can receive one as children or as a prop, since that content is already rendered on the server.

// A client wrapper that adds behaviour around server-rendered content
'use client'

import { useState, type ReactNode } from 'react'

export function Collapsible({ title, children }: { title: string; children: ReactNode }) {
  const [open, setOpen] = useState(false)

  return (
    <section>
      <button onClick={() => setOpen(!open)}>{title}</button>
      {open ? children : null}
    </section>
  )
}
// Server Component using it
export default async function Page() {
  const details = await getDetails() // server-side data

  return (
    <Collapsible title="Details">
      <DetailsList items={details} /> {/* rendered on the server */}
    </Collapsible>
  )
}

Passing Data Across the Boundary

Props passed from a Server Component to a Client Component must be serializable. Strings, numbers, booleans, arrays, plain objects, Date and a few others are fine. Functions, class instances and anything with methods are not.

A Server Action is the exception: you can pass one as a prop to run server code from a client event. For when to use one, see our guide on Server Actions vs API Routes in Next.js.

Keeping Server Code Out of the Client

A server-only module imported by mistake into a client file can leak a secret or break the build. The server-only package turns that mistake into a build error:

// lib/db.ts
import 'server-only'

export const db = createDatabaseClient(process.env.DATABASE_URL)

Importing lib/db.ts from a Client Component now fails at build time.

Context and Third-Party Libraries

React context does not work in Server Components. A provider is a Client Component, and it should wrap only what needs it, usually near the root:

// app/providers.tsx
'use client'

import { ThemeProvider } from 'some-theme-library'
import type { ReactNode } from 'react'

export function Providers({ children }: { children: ReactNode }) {
  return <ThemeProvider>{children}</ThemeProvider>
}

The children passed through it can still be Server Components. Wrapping them in a client provider does not turn them into client code.

A library that uses hooks but has no 'use client' directive of its own needs a small wrapper file that adds it.

Common Mistakes

  • Adding 'use client' to the page or layout. The entire subtree joins the bundle. Move it to the smallest component that needs it.
  • Fetching in a Client Component with useEffect for data that could load on the server. It adds a loading state and a request waterfall.
  • Passing a function or class instance as a prop across the boundary. Only serializable values, and Server Actions, cross it.
  • Importing a server-only module into a client file. Use server-only to catch it.
  • Reading window in a Server Component. It does not exist on the server.
  • Assuming Client Components skip server rendering. They render on the server for the first load, so browser-only code needs an effect or a guard.

A Quick Checklist

Before adding 'use client', ask:

  1. Does this component use state, effects or event handlers?
  2. Does it need a browser API?
  3. Could the interactive part be a small child component instead?

If the answer to the first two is no, leave it on the server. If the answer to the third is yes, split it.

Summary

Server Components fetch data and render without adding to the browser bundle. Client Components add interactivity and ship their code to the browser. Build the page from Server Components, and add small Client Components only where the user needs to interact.

#Next.js#React#Server Components#Client Components#App Router#Web Development

Responses

M↓

Get Started on a Project?

Let's Talk

Start the
Journey
dynavolabs.info@gmail.comStart the Journey