Error: 'generateMetadata' cannot be used in a Client Component
ID: nextjs/generate-metadata-client-component
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 14 | active | — | — | — |
Root Cause
The generateMetadata function or metadata export is placed in a file with the 'use client' directive. In the Next.js App Router, metadata generation is inherently a server-side operation because it must run during SSR/SSG to inject <head> tags. Client Components cannot export generateMetadata or the static metadata object. The solution is always to keep metadata exports in Server Components.
genericWorkarounds
-
95% success Split the file into a Server Component page with metadata and a separate Client Component for interactivity
Separate server metadata from client interactivity: // app/dashboard/page.tsx (Server Component - NO 'use client') import type { Metadata } from 'next'; import DashboardClient from './dashboard-client'; export const metadata: Metadata = { title: 'Dashboard', description: 'User dashboard with analytics', }; // OR use generateMetadata for dynamic metadata: export async function generateMetadata({ params }): Promise<Metadata> { const data = await fetchData(params.id); return { title: data.name, description: data.summary }; } export default function DashboardPage() { return <DashboardClient />; } // app/dashboard/dashboard-client.tsx 'use client'; import { useState } from 'react'; export default function DashboardClient() { const [tab, setTab] = useState('overview'); return <div>...</div>; }Sources: https://nextjs.org/docs/app/building-your-application/optimizing/metadata https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns
-
93% success Use generateMetadata with async data fetching in the server page component
For dynamic metadata that depends on route params or fetched data: // app/products/[id]/page.tsx (Server Component) import type { Metadata, ResolvingMetadata } from 'next'; type Props = { params: { id: string } }; export async function generateMetadata( { params }: Props, parent: ResolvingMetadata ): Promise<Metadata> { const product = await fetch(`https://api.example.com/products/${params.id}`).then(r => r.json()); const previousImages = (await parent).openGraph?.images || []; return { title: product.name, openGraph: { images: [product.image, ...previousImages], }, }; } export default async function ProductPage({ params }: Props) { const product = await fetch(`https://api.example.com/products/${params.id}`).then(r => r.json()); return <ProductView product={product} />; // ProductView can be 'use client' }Sources: https://nextjs.org/docs/app/api-reference/functions/generate-metadata
Dead Ends
Common approaches that don't work:
-
Moving 'use client' below the generateMetadata export to try to make it server-only
95% fail
The 'use client' directive must be at the top of a file and applies to the entire module. There is no way to partially mark a file as client or server. Moving the directive below the export does not change its scope -- the entire file is still a Client Component, and generateMetadata remains invalid.
-
Using document.title or useEffect to set metadata dynamically on the client
80% fail
Setting document.title via useEffect bypasses Next.js metadata system entirely. Search engine crawlers and social media scrapers will not see the metadata because it requires JavaScript execution. This breaks SEO, Open Graph tags, and social sharing cards. The metadata will also flash/change after hydration, causing poor user experience.