What This Next.js Metadata Error Actually Means
You add a "use client" directive to a page. You also have a metadata export on the same file. Next.js throws this error and your build breaks.
You are attempting to export metadata from a component marked with "use client",
which is disallowed. Either remove the metadata export or the "use client" directive.
This is one of the most common errors developers hit when working with the Next.js App Router โ especially when migrating from Pages Router or adding interactivity to a page that previously had no client-side code.
The error is clear but the fix requires understanding why Next.js enforces this rule in the first place. This guide explains the root cause, shows every scenario that triggers it, and gives you the exact fix for each one.
๐ฏ Quick Answer (30-Second Read)
- Root cause:
metadatais a server-only export โ it cannot exist in a Client Component marked with"use client" - Most common trigger: Adding
"use client"to a page file that already has ametadataexport - Fix 1: Remove
"use client"from the page and move client logic into a separate child component - Fix 2: Keep the page as a Server Component, extract the interactive part into a
"use client"child component - Fix 3: Use
generateMetadatafunction instead of staticmetadataobject โ still server-only, same rule applies - Golden rule: Page files with
metadataorgenerateMetadatamust always be Server Components
Why Next.js Does Not Allow Metadata in Client Components
Metadata โ page titles, descriptions, Open Graph tags โ is rendered in the HTML <head> on the server before the page reaches the browser. It needs to exist at request time, not after JavaScript hydrates.
Client Components run in the browser. They execute after the server has already sent the HTML response. By the time a Client Component runs, the <head> has already been built and sent. Any metadata export in a Client Component would be meaningless โ the server would never see it.
This is not a framework limitation. It is a fundamental timing constraint of how server-rendered HTML works.
Next.js enforces this at build time rather than letting it silently fail at runtime โ which is why you see a hard error instead of just missing metadata in production.
Every Scenario That Causes This Error
Scenario 1 โ Adding "use client" directly to a page file
The most common trigger. You need a useState or useEffect on a page, so you add "use client" at the top:
// app/dashboard/page.tsx โ THIS BREAKS
"use client"
import { useState } from "react"
export const metadata = {
title: "Dashboard",
description: "Your personal dashboard"
}
export default function DashboardPage() {
const [count, setCount] = useState(0)
return {count}
}Scenario 2 โ Re-exporting from a client component file
A layout or page imports and re-exports metadata from a file that has "use client":
// components/PageWrapper.tsx โ THIS BREAKS
"use client"
export const metadata = {
title: "Page Title"
}
export default function PageWrapper({ children }) {
return {children}
}Scenario 3 โ generateMetadata in a client component
The dynamic metadata function has the same constraint as the static object:
// app/blog/[slug]/page.tsx โ THIS ALSO BREAKS
"use client"
export async function generateMetadata({ params }) {
return { title: params.slug }
}
export default function BlogPost() {
// component code
}The Fix โ Three Approaches
Fix 1 โ Extract Client Logic Into a Child Component (Recommended)
This is the cleanest fix and the pattern Next.js App Router is designed around. Keep the page file as a Server Component. Move all client-side logic into a separate component with "use client".
// app/dashboard/page.tsx โ SERVER COMPONENT โ
import { DashboardClient } from "@/components/DashboardClient"
export const metadata = {
title: "Dashboard",
description: "Your personal dashboard"
}
export default function DashboardPage() {
return
}// components/DashboardClient.tsx โ CLIENT COMPONENT โ
"use client"
import { useState } from "react"
export function DashboardClient() {
const [count, setCount] = useState(0)
return (
{count}
)
}The page file owns the metadata. The client component owns the interactivity. Neither conflicts with the other.
Fix 2 โ Pass Server Data as Props to the Client Component
When the page needs to fetch data server-side and pass it to an interactive client component:
// app/profile/page.tsx โ SERVER COMPONENT โ
import { ProfileClient } from "@/components/ProfileClient"
import { getUser } from "@/lib/db"
export const metadata = {
title: "Profile",
description: "Your profile page"
}
export default async function ProfilePage() {
// Data fetching happens on the server
const user = await getUser()
// Pass data as props to client component
return
}// components/ProfileClient.tsx โ CLIENT COMPONENT โ
"use client"
import { useState } from "react"
export function ProfileClient({ user }) {
const [editing, setEditing] = useState(false)
return (
{user.name}
)
}Fix 3 โ Use generateMetadata for Dynamic Metadata
When metadata depends on route params or fetched data, generateMetadata works the same way as the static export โ it must stay in a Server Component:
// app/blog/[slug]/page.tsx โ SERVER COMPONENT โ
import { PostClient } from "@/components/PostClient"
import { getPost } from "@/lib/db"
// generateMetadata is server-only โ no "use client" on this file
export async function generateMetadata({ params }) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
images: [post.coverImage]
}
}
}
export default async function BlogPostPage({ params }) {
const post = await getPost(params.slug)
return
}The Mental Model That Prevents This Error Forever
Every time you work on a Next.js App Router page, ask one question:
Does this file export metadata or generateMetadata?
If yes โ this file is a Server Component. It cannot have "use client". Full stop.
Any interactivity this page needs goes into a child component with "use client". The page file is the shell. The client component is the interactive layer inside it.
Page file (Server Component)
โโโ metadata export โ
โโโ async data fetching โ
โโโ "use client" โ โ never on the same file as metadata
โโโ renders ClientComponent
โโโ ClientComponent.tsx
โโโ "use client" โ
โโโ useState, useEffect โ
โโโ event handlers โ
This pattern works for every page in your application. Once it is internalised, this error never appears again.
Common Mistakes After Applying the Fix
Putting "use client" at the top of a layout file that has metadata
Layout files follow the same rule. app/layout.tsx often has metadata โ it cannot have "use client". If your layout needs client-side logic, extract it into a ClientLayout component and render it inside the server layout.
Adding metadata to a component that is imported into a client component tree
Metadata only works in page and layout files. Adding a metadata export to a regular component โ even a server one โ does not do anything. Metadata must be exported from page.tsx or layout.tsx files specifically.
Forgetting that Context Providers need "use client"
Most apps wrap their layout with a Context Provider for themes, auth state, or toast notifications. Context Providers require "use client". The fix is to create a Providers.tsx client component, wrap children inside it, and render it from the server layout โ keeping the layout itself a Server Component with metadata intact.
// app/layout.tsx โ SERVER COMPONENT โ
import { Providers } from "@/components/Providers"
export const metadata = {
title: "My App",
description: "My application"
}
export default function RootLayout({ children }) {
return (
{children}
)
}// components/Providers.tsx โ CLIENT COMPONENT โ
"use client"
import { ThemeProvider } from "next-themes"
import { Toaster } from "sonner"
export function Providers({ children }) {
return (
{children}
)
}Real Developer Use Case
A developer adding analytics tracking to a Next.js marketing page hit this error. The page had a metadata export with full Open Graph configuration. They needed useEffect to fire a page view event to their analytics provider on mount.
The instinct was to add "use client" to the page. The build broke immediately.
The fix was a three-line AnalyticsTracker component:
// components/AnalyticsTracker.tsx
"use client"
import { useEffect } from "react"
import { trackPageView } from "@/lib/analytics"
export function AnalyticsTracker({ page }: { page: string }) {
useEffect(() => {
trackPageView(page)
}, [page])
return null // renders nothing โ side effect only
}Rendered inside the page: <AnalyticsTracker page="home" />. The metadata stayed intact. The analytics fired on mount. Zero changes to the page file structure โ just one new component file.
This pattern โ a render-nothing client component for side effects โ solves an entire category of "I just need useEffect on this page" problems without touching the server component boundary.
Frequently Asked Questions
Can I have both metadata and client-side code in the same page at all?
Not in the same file. The page file must be a Server Component to export metadata. But you can have rich client-side interactivity on that page โ just inside child components marked with "use client". The page renders server-side, the client components hydrate in the browser. Both exist on the same page, just in separate files.
Does this error occur in the Pages Router?
No. The Pages Router does not have the Server Component / Client Component distinction. The metadata export system is App Router-specific. If you are on Pages Router, you use next/head for metadata, which works in any component. This error only appears when you are using the App Router introduced in Next.js 13.
What if I need the metadata to depend on client-side state?
You cannot. Metadata is generated server-side at request time โ it cannot depend on state that only exists in the browser. If you need dynamic metadata, use generateMetadata with server-side data sources (database, API, route params). Client-side state like user preferences cannot influence server-rendered metadata.
Why does Next.js not just ignore the metadata export in client components instead of throwing an error?
Because silently ignoring it would mean your page ships with missing or incorrect metadata โ affecting SEO, social sharing, and browser tab titles โ with no indication of why. A hard build error surfaces the problem immediately when it can be fixed, rather than silently at runtime when it affects real users and search rankings.
Does this apply to the "use server" directive too?
The inverse problem does not exist โ Server Components cannot use hooks or browser APIs, but there is no metadata restriction on them. The constraint is one-directional: metadata requires server, hooks require client, and those two requirements cannot coexist in the same file.
Conclusion
The metadata client component error in Next.js has one root cause and one fix pattern: metadata is server-only, so any file exporting it must be a Server Component, and any interactivity that file needs must live in a child client component.
The mental model is simple โ page files own metadata, client components own interactivity, and those two responsibilities never share a file.
Apply this pattern once, internalise the boundary, and this error disappears from your workflow permanently.
Related reads: How to Use Environment Variables in Next.js ยท How to Create a SaaS with Next.js and Supabase ยท How to Deploy Next.js on Vercel Step-by-Step ยท How Developers Use AI to Build Apps Faster