When to Use 'use client' in Next.js

Server vs client components in Next.js 13+. When you need 'use client', when you don't, and what breaks if you get it wrong.

One of the biggest changes in Next.js 13+ is the introduction of React Server Components. This paradigm shift can be confusing for developers migrating from traditional React, but understanding it is crucial for building performant Next.js applications.

The Fundamental Difference

Server Components

  • Render on the server
  • No JavaScript sent to client
  • Can access backend resources
  • Default in Next.js 13+
  • Cannot use hooks or browser APIs

Client Components

  • Render on client
  • JavaScript sent to browser
  • Can use React hooks
  • Need 'use client' directive
  • Can handle interactivity

Server Components: The Default

In Next.js 13+, all components are server components by default. They render on the server and send only HTML to the client.

// app/products/page.js // This is a Server Component (default) async function getProducts() { const res = await fetch('https://api.example.com/products'); return res.json(); } export default async function ProductsPage() { const products = await getProducts(); return ( <div> <h1>Products</h1> {products.map(product => ( <div key={product.id}> <h2>{product.name}</h2> <p>${product.price}</p> </div> ))} </div> ); }

Benefits of Server Components

Client Components: When You Need Interactivity

Use client components when you need interactivity, browser APIs, or React hooks like useState and useEffect.

// components/Counter.js 'use client'; import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }

When to Use Client Components

Composing Server and Client Components

The real power comes from combining both types. A common pattern is to have server components fetch data and pass it to client components for interactivity.

// app/dashboard/page.js (Server Component) import ClientChart from '@/components/ClientChart'; async function getData() { const res = await fetch('https://api.example.com/analytics'); return res.json(); } export default async function Dashboard() { const data = await getData(); return ( <div> <h1>Dashboard</h1> {/* Server-rendered content */} <p>Total Users: {data.totalUsers}</p> {/* Client component for interactivity */} <ClientChart data={data.chartData} /> </div> ); }
// components/ClientChart.js (Client Component) 'use client'; import { useState } from 'react'; import { LineChart } from 'recharts'; export default function ClientChart({ data }) { const [timeRange, setTimeRange] = useState('week'); return ( <div> <select onChange={(e) => setTimeRange(e.target.value)}> <option value="week">Last Week</option> <option value="month">Last Month</option> </select> <LineChart data={data} /> </div> ); }

Common Patterns and Best Practices

Pattern 1: Keep Client Components Small

Push 'use client' as far down the component tree as possible.

// Problem: Bad: Entire page is client component 'use client'; export default function Page() { const [search, setSearch] = useState(''); return ( <div> <Header /> <SearchBar value={search} onChange={setSearch} /> <ProductList /> <Footer /> </div> ); } // Solution: Good: Only interactive part is client component export default function Page() { return ( <div> <Header /> <SearchBar /> {/* Only this needs 'use client' */} <ProductList /> <Footer /> </div> ); }

Pattern 2: Pass Server Data to Client Components

Fetch data in server components and pass it as props to client components.

// app/users/page.js (Server) async function getUsers() { const res = await fetch('https://api.example.com/users'); return res.json(); } export default async function UsersPage() { const users = await getUsers(); return <UserTable users={users} />; } // components/UserTable.js (Client) 'use client'; export default function UserTable({ users }) { const [sortBy, setSortBy] = useState('name'); const sortedUsers = [...users].sort((a, b) => a[sortBy].localeCompare(b[sortBy]) ); return ( <table> {/* Interactive sorting */} </table> ); }

Pattern 3: Use Context Providers Wisely

Context providers must be client components, but you can wrap them strategically.

// app/layout.js (Server) import { ThemeProvider } from '@/components/ThemeProvider'; export default function RootLayout({ children }) { return ( <html> <body> <ThemeProvider> {children} </ThemeProvider> </body> </html> ); } // components/ThemeProvider.js (Client) 'use client'; import { createContext, useState } from 'react'; export const ThemeContext = createContext(); export function ThemeProvider({ children }) { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider> ); }

Pro Tip: You cannot import server components into client components, but you can pass them as children or props!

Decision Tree: Which Component Type?

Use this simple decision tree:

Common Mistakes to Avoid

Mistake 1: Using Hooks in Server Components

// Problem: Error: Can't use hooks in server components export default function Page() { const [data, setData] = useState([]); // Error! return <div>{data}</div>; } // Solution: Use async/await instead export default async function Page() { const data = await fetchData(); return <div>{data}</div>; }

Mistake 2: Making Everything a Client Component

Don't add 'use client' to everything just to avoid errors. This defeats the purpose of server components and hurts performance.

Mistake 3: Importing Server Components into Client Components

// This won't work 'use client'; import ServerComponent from './ServerComponent'; export default function ClientComponent() { return <ServerComponent />; // Error! } // Solution: Pass as children instead 'use client'; export default function ClientComponent({ children }) { return <div>{children}</div>; }

Migrate to Next.js with Confidence

ReactToNext automatically handles server/client component conversion, making your migration smooth and error-free.

Try ReactToNext Free →

Conclusion

Understanding server vs client components is fundamental to building modern Next.js applications. The key is to:

With practice, choosing the right component type becomes second nature, and you'll build faster, more efficient applications.