Module 4: Client Interactivity

Using Client Components effectively in the App Router

The 'use client' Directive

To use React hooks (useState, useEffect) or event listeners, you must mark a component as a Client Component.

'use client' // Must be at the very top

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}

Note: "Client Component" doesn't mean it only runs on the client. It is pre-rendered on the server (SSR) and then hydrated on the client.

When to use Client Components?

Use Case Server Component Client Component
Fetch data ✅ (Preferred) ⚠️ (Use SWR/React Query)
Access Backend Resources (DB)
Keep sensitive info (API keys)
Add interactivity (onClick)
Use State / Lifecycle Effects
Use Browser APIs (localStorage)

Composition Patterns

Avoid making everything a Client Component. Move interactivity to the leaves of your component tree.

Passing Server Components as Props

You cannot import a Server Component into a Client Component directly if you need it to remain a Server Component. Instead, pass it as children.

// app/page.js (Server)
import ClientWrapper from './client-wrapper'
import ServerContent from './server-content'

export default function Page() {
  return (
    <ClientWrapper>
      <ServerContent /> {/* Remains a Server Component! */}
    </ClientWrapper>
  )
}
// app/client-wrapper.js (Client)
'use client'
import { useState } from 'react'

export default function ClientWrapper({ children }) {
  const [open, setOpen] = useState(false)
  return (
    <div>
      <button onClick={() => setOpen(!open)}>Toggle</button>
      {open && children}
    </div>
  )
}

Next.js Specific Hooks

Hooks from next/navigation are for Client Components.