Full-Stack

Stop Overusing 'use client' in Next.js 16

13 min read
MH
Mussawar Hayat

The most common App Router mistake is marking entire trees with use client. Learn the production patterns for Server Components, children slots, and minimal client boundaries that keep your bundles small and your data secure.

The Default Is Already Correct

In Next.js 16 every layout and page in the app/ directory is a Server Component by default. That is the right default. Server Components render on the server, send zero JavaScript for their own logic, can talk to databases and secrets directly, and stream HTML to the browser. The moment you add 'use client' higher than necessary, you forfeit those benefits for an entire subtree.

What You Will Learn

  • When 'use client' is actually required
  • The children-slot pattern that keeps Server Components inside Client Components
  • How to wrap third-party interactive libraries without poisoning the tree
  • Context providers that stay as deep as possible
  • A practical decision checklist for every new component

1. The Real Cost of an Unnecessary Client Boundary

Once a file is marked 'use client', every module it imports and every component it renders directly becomes part of the client bundle. That includes heavy libraries you only needed on the server, database helpers that should never ship, and pure presentational components that could have stayed server-only.

The boundary is one-way. Server Components can import Client Components. Client Components cannot import Server Components. The only safe way to interleave them is to pass Server Components as children or as props from a parent Server Component.

2. Decision Tree: Server or Client?

Ask these questions in order:

  1. Does the component need state, event handlers, or browser APIs (window, localStorage, useEffect)? → Client Component.
  2. Does it only fetch data, render static markup, or use secrets? → Server Component (leave the default).
  3. Does a third-party package require client features? → Create a thin Client wrapper; keep the rest of the tree server-side.

Most leaves that need interactivity (buttons, forms, modals, theme toggles) are Client Components. Everything above them should stay Server Components.

3. The Children-Slot Pattern (Production Standard)

This is the pattern official Next.js docs recommend and the one that appears in every well-structured App Router codebase.

Create a Client Component that accepts children:

TypeScript components/modal.tsx
'use client'

import { useState } from 'react'

export function Modal({ children }: { children: React.ReactNode }) {
  const [open, setOpen] = useState(false)

  return (
    <>
      <button onClick={() => setOpen(true)}>Open</button>
      {open && (
        <div role="dialog" className="modal">
          {children}
          <button onClick={() => setOpen(false)}>Close</button>
        </div>
      )}
    </>
  )
}

Then compose from a Server Component page:

TypeScript app/dashboard/page.tsx
import { Modal } from '@/components/modal'
import { Cart } from '@/components/cart' // Server Component — fetches from DB

export default async function DashboardPage() {
  return (
    <Modal>
      <Cart /> {/* still rendered on the server */}
    </Modal>
  )
}

Cart never becomes part of the client module graph. Its output is already rendered in the RSC payload and simply slotted into the Client Component.

4. Context Providers Belong Deep in the Tree

React context only works in Client Components. Wrap only the subtree that needs the context, not the entire document.

TypeScript app/theme-provider.tsx
'use client'

import { createContext, useContext, useState } from 'react'

const ThemeContext = createContext<{ theme: string; toggle: () => void } | null>(null)

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState('dark')
  return (
    <ThemeContext.Provider value={{ theme, toggle: () => setTheme(t => t === 'dark' ? 'light' : 'dark') }}>
      {children}
    </ThemeContext.Provider>
  )
}

export function useTheme() {
  const ctx = useContext(ThemeContext)
  if (!ctx) throw new Error('useTheme must be used inside ThemeProvider')
  return ctx
}

In the root layout keep the provider around {children} only — never around <html> or <body> if you can avoid it. That leaves static shell pieces free for Next.js to optimise.

5. Third-Party Interactive Libraries

Many packages (carousels, rich text editors, charts) still ship without a 'use client' directive. Importing them directly into a Server Component fails. The fix is a one-line re-export:

TypeScript components/carousel.tsx
'use client'

export { Carousel } from 'acme-carousel'

You can now safely use <Carousel /> from any Server Component. The rest of the page stays server-rendered.

6. Common Mistakes That Still Appear in Production

  • Putting 'use client' on a layout or page just to use one interactive child.
  • Importing a Prisma client or secret-bearing module into a Client Component.
  • Creating a new Client boundary for every small interactive element instead of one shared provider or shell.
  • Passing non-serialisable props (functions, class instances, Dates without conversion) from Server to Client.
  • Relying on middleware alone for auth while the interactive form itself lives in a Client Component that never re-checks the session.

Summary

Treat 'use client' as a precision tool, not a default. Keep Server Components as high as possible, push Client Components to the leaves, and use the children-slot pattern to compose them. The result is smaller bundles, faster First Contentful Paint, secrets that stay on the server, and an architecture that survives future Next.js releases.

Key Takeaway

Default to Server Components. Add 'use client' only when the component truly needs the browser. Pass Server Components as children into Client shells. That single habit eliminates the majority of App Router performance and security regressions.


Need a production review of your App Router architecture?

I audit Next.js 16 codebases for unnecessary client boundaries, data-access patterns, and security posture. Get in touch if you want a focused pass on your Server/Client split.

Ready to start your project?

Let's discuss how we can transform your digital presence with cutting-edge solutions.