SyntaxStudy
Sign Up
Next.js The use client Directive and Client Component Patterns
Next.js Beginner 1 min read

The use client Directive and Client Component Patterns

The 'use client' directive marks a file as a Client Component module boundary. When Next.js encounters this directive it includes the component and all its imports in the client JavaScript bundle. Everything imported by a Client Component is also treated as client code, so you must be careful not to import server-only modules (like database drivers) from Client Component files. A common architectural mistake is placing 'use client' at the top of large parent components that contain mostly static content. This forces all child components into the client bundle unnecessarily. The recommended pattern is to extract the interactive parts into small, focused Client Component files and keep the surrounding structure as Server Components. Context providers are a special case — they must be Client Components, so wrap only the necessary subtree rather than the entire app. Third-party components that use React hooks or browser APIs (useState, useEffect, window, document) need 'use client' but may not ship it themselves. In that case wrap them in your own Client Component file that re-exports them. This is a common pattern when integrating UI libraries like Radix UI, Framer Motion, or React Hook Form into an App Router project.
Example
// Wrapping a third-party component that lacks 'use client'
// components/providers/ThemeProvider.tsx
'use client';

// next-themes doesn't always ship with 'use client', so we wrap it
export { ThemeProvider } from 'next-themes';

// app/layout.tsx — using the wrapped provider
import { ThemeProvider } from '@/components/providers/ThemeProvider';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        {/* ThemeProvider is Client but wraps Server Component children */}
        <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

// Passing Server Component as children to Client Component
// components/Modal.tsx
'use client';

import { useState } from 'react';

export default function Modal({ children }: { children: React.ReactNode }) {
  const [open, setOpen] = useState(false);
  return (
    <>
      <button onClick={() => setOpen(true)}>Open</button>
      {open && (
        <div className="modal-overlay">
          <div className="modal-content">
            <button onClick={() => setOpen(false)}>Close</button>
            {/* children can be a Server Component rendered result */}
            {children}
          </div>
        </div>
      )}
    </>
  );
}