Adding 'use client' unnecessarily ships component JS to the browser. Only use it when you need hooks, event handlers, or browser APIs.
reduces JavaScript bundle sent to browser, improves load times
BeforeMerge scans your pull requests against this rule and 5+ others. Get actionable feedback before code ships.
Impact: HIGH (reduces JavaScript bundle sent to browser, improves load times)
In the Next.js App Router, components are Server Components by default. Adding 'use client' sends the component's JavaScript to the browser, increasing bundle size and Time to Interactive. Only add the client directive when you actually need browser APIs, event handlers, or React hooks that require client-side execution.
Incorrect (unnecessary 'use client'):
// ❌ This component has no interactivity — why is it a client component?
'use client'
export function UserProfile({ user }: { user: User }) {
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
<img src={user.avatarUrl} alt={user.name} />
</div>
)
}// ❌ Marking the entire page as client just because one small part needs interactivity
'use client'
import { useState } from 'react'
export default function ProductPage({ product }: { product: Product }) {
const [showDetails, setShowDetails] = useState(false)
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p> {/* Static */}
<ProductSpecs specs={product.specs} /> {/* Static */}
<ReviewsList reviews={product.reviews} /> {/* Static */}
<button onClick={() => setShowDetails(!showDetails)}>
{showDetails ? 'Hide' : 'Show'} Details
</button>
{showDetails && <Details product={product} />}
</div>
)
}Correct (push 'use client' to the leaf):
// ✅ Page stays as Server Component — zero JS sent for static content
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await fetchProduct(params.id)
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<ProductSpecs specs={product.specs} />
<ReviewsList reviews={product.reviews} />
{/* Only this small piece is a Client Component */}
<DetailsToggle product={product} />
</div>
)
}
// components/DetailsToggle.tsx
'use client'
import { useState } from 'react'
export function DetailsToggle({ product }: { product: Product }) {
const [showDetails, setShowDetails] = useState(false)
return (
<>
<button onClick={() => setShowDetails(!showDetails)}>
{showDetails ? 'Hide' : 'Show'} Details
</button>
{showDetails && <Details product={product} />}
</>
)
}When you DO need 'use client':
useState, useReducer, useEffect, useRef and other React hooksonClick, onChange, onSubmit)window, document, localStorage, navigator)Detection hints:
# Find client components that might not need to be
grep -rn "'use client'" src/ --include="*.tsx" -l | while read f; do
if ! grep -qE "useState|useEffect|useReducer|useRef|useCallback|useMemo|onClick|onChange|onSubmit|addEventListener" "$f"; then
echo "Possibly unnecessary 'use client': $f"
fi
doneReference: Next.js Server and Client Components