Back to Next.js tutorials
Intermediate13 min read

Client Components

Interactive UI with hooks, browser APIs, and event handlers — scoped behind the client boundary.

When You Need Client Components

Add `"use client"` at the top of files that use useState, useEffect, useReducer, useContext, or event handlers (onClick, onChange). Browser-only APIs — localStorage, window, geolocation, IntersectionObserver — require client execution.

Third-party libraries built for SPA React often assume client environment. Wrap them in thin client wrapper components rather than marking entire pages client.

  • Hooks and events require client
  • Browser APIs require client
  • Wrap third-party widgets in client shells
'use client';
import { useState } from 'react';
export function Counter() {
  const [n, setN] = useState(0);
  return <button onClick={() => setN(n + 1)}>{n}</button>;
}

Minimize Client Bundle Impact

Push the client boundary as deep as possible. A page with one interactive button should not mark the entire page client — extract the button to `SubmitButton.tsx` with `"use client"` and import it into the server page.

Analyze bundles with `@next/bundle-analyzer`. Client imports pull their dependency trees into the browser bundle — heavy charting libraries belong in dynamically imported client components.

  • Client boundary as deep as possible
  • Dynamic import for heavy client libs
  • Analyze bundle impact per client file

Providers and Context

Context providers must be Client Components because context uses React state internally. Place ThemeProvider, QueryClientProvider, and SessionProvider in a client layout wrapper near root.

Pass server-fetched initial data as props into providers that hydrate client cache (TanStack Query dehydrate/hydrate pattern). Avoid fetching the same data twice — server fetch once, hydrate client.

  • Providers are client components
  • Hydrate client cache from server props
  • Avoid duplicate server/client fetching

Interactivity with Server Data

Server Components pass fetched data as props to Client Components for interactive rendering — charts, sortable tables, filters. Mutations call Server Actions or Route Handlers rather than exposing database credentials client-side.

Optimistic updates with useOptimistic improve mutation UX while server confirms. Roll back optimistic state on server error responses.

  • Server data as props to client UI
  • Mutations via Server Actions
  • useOptimistic for mutation UX

Testing Client Components

Test client components with React Testing Library in jsdom. Mock Server Actions and next/navigation hooks. Integration tests render client components within provider wrappers matching production.

Storybook documents client component states — interactions, loading, error. Keep stories colocated with components for design review.

  • RTL with mocked navigation/actions
  • Provider wrappers in tests
  • Storybook for interaction states

Get In Touch


Ready to discuss your next project? Drop me a message.