How to set page title and description if page is "use client" ? #50872
|
Hi, The only solution I find actually is to have a "dump" Is the new Thanks ! |
Replies: 27 comments 35 replies
|
Yeah, refactoring seems be the way to go. Keep the page a server component, add the metadata there, and import the client component to it. Makes you miss how flexible the Head component was, but at the same time, it sort of makes sense to separate these... at least if the general design pattern is to push client components to the leaves of the React tree. An update on this: The Metadata API exists to help Next.js manage and compose the metadata for a route. However, React itself also supports rendering document metadata, so yes — you can totally inline: <title>Hello world</title>in your JSX. React will hoist it into the document So for a page that has The main tradeoff is that you're managing the title from within the component tree rather than through Next.js' route-level metadata system, and the two don't compose — they just both emit. You need to be disciplined about ownership: if multiple components render For most pages I'd still lean toward keeping the page as a Server Component and using the Metadata API, with Client Components pushed further down the tree. |
|
Put the metadata in nested layouts, details here: Nesting Layouts Works for me. |
|
My layout renders on the client side, for route info needs, and I just used the tag after. and it worked just fine. |
|
Guys, WTF |
|
In my case I multi languages titles, I ended creating hook to change title. mounted state to be sure that the code running in client side. usage |
|
WTF. The |
|
Sometimes we will need to build "absolute" client components (account page, edit profile, orders list...). It will be very bad if you cannot at least change the title of these pages. I know somebody will say that don't use nextjs for that situation... I'm fine! |
|
Yeah this is pretty ridiculous. For page titles in client components, you can use a <title></title> tag within the component. For descriptions....yeah there doesn't seem to be a way to do this besides the whacky work-around of nesting a client component in a server component. Dropping the ball daily @vercel , great stuff. |
|
Definitions complicate things that are actually easy |
|
I think they've now added in a solution to getting searchParams paramThere's now a searchParams param you can add into your generateMetadata function but it has to be done in a server component: generateMetadata({ params, searchParams }) {}One gotcha is that it works differently than the client side searchParams in that you don't get the full web API to look through params. So if you had a route like: Example UsageMy scenario is that I have a digest of articles accessible under one path, so my data payload for a month edition of my magazine is like 4-5 articles that require client navigation. My paths are like export async function generateMetadata({ params, searchParams }) {
// Dynamic data fetch
const metadata = await getArticlesMetadata(params);
// Pulling apart my issue and article responses
const { allArticles, issue } = metadata;
// Getting current post metadata
const currentSlug = searchParams.post;
let currentArticle;
// Filter my article to find the matching slug (note how I did fallbacks, might not work for you)
const filtered = allArticles.filter((article) => article.slug === currentSlug);
if (filtered.length > 0) {
currentArticle = filtered[0];
} else {
currentArticle = allArticles[0];
}
return {
title: `${currentArticle.title} - Mag Name HERE`,
description: currentArticle.intro,
generator: 'Next.js 14 / React',
applicationName: 'Mag Name HERE',
referrer: 'origin-when-cross-origin',
keywords: [
'whatever',
'you',
'need',
],
publisher: 'ABCXYZ',
metadataBase: new URL('https://www.example.com'),
openGraph: {
title: `${currentArticle.title} - Mag Name HERE`,
description: currentArticle.intro,
url: `https://www.example.com/category/${issue.slug}?post=${currentArticle.slug}`,
siteName: 'Example Mag',
images: [
{
url: currentArticle.mainImageUrl,
width: 1200,
height: 630,
alt: 'Example Mag Image',
},
],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: `${currentArticle.title} - Mag Name HERE`,
description: currentArticle.intro,
creator: '@yourhandle',
creatorId: '########',
siteId: '########',
images: [currentArticle.mainImageUrl],
},
robots: {
index: true,
},
icons: {
icon: '/images/favicon/Favicon_64x64.png',
apple: '/images/favicon/Fav_Green_180x180.png',
},
appLinks: {
web: {
url: 'https://www.example.com',
should_fallback: true,
},
},
};
} |
|
In my Next.js React app I found a work around by getting the document.title and using a useEffect hook to stop it instantly changing back: Someone feel free to add to this if this has any side effects? |
|
I want to change document.title based on searchParams, how to achieve this? |
|
Its very simple to add title description and also the OG images without using metadata export and without using the nested layouts. Example : this is very simple way i also checked the description by sending the page link to whatsapp the whatsapp showing the description. |
|
its not this simple some random build issues come which is unable to find
and fix
…On Wed, 24 Jul 2024 at 7:18 PM, Aayush Khatri ***@***.***> wrote:
Its very simple to add title description and also the OG images without
using metadata export and without using the nested layouts.
Example :
`
'use client'
export default function page({params}) {
return (
// this is your main div wrapping all the content of your page
<title>This is title</title>
// your all content
</div>
)
}
`
this is very simple way i also checked the description by sending the page
link to whatsapp the whatsapp showing the description.
—
Reply to this email directly, view it on GitHub
<#50872 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BH74LE7OVJT3NGAZWAVWJCLZN6WBJAVCNFSM6AAAAAAY47QJTWVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTAMJTHAYDINA>
.
You are receiving this because you commented.Message ID: <vercel/next.
***@***.***>
|
|
Why are we forced to jump through hoops to get a simple thing like this working on the client-side? I feel like @vercel pushes that there are no SSR and CSR use cases, but that SSR is good, and CSR is bad. |
|
|
|
To dynamically change the title tag in a “use client” component based on custom hook logic, I implemented a simple context-based solution. src/components/head-title/provider.tsx "use client";
import { createContext, useState } from "react";
export const HeadTitleContext = createContext<{
title: string;
setTitle: (title: string) => void;
}>({
title: "",
setTitle: () => {},
});
export const HeadTitleProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const [title, setTitle] = useState<string>("");
return (
<HeadTitleContext.Provider value={{ title, setTitle }}>
{children}
</HeadTitleContext.Provider>
);
};With this context, I created a HeadTitle component and a custom hook to manage the title. src/components/head-title/index.tsx "use client";
import { useContext } from "react";
import { HeadTitleContext } from "./provider";
export { HeadTitleProvider } from "./provider";
export const useHeadTitle = () => useContext(HeadTitleContext);
export const HeadTitle = () => {
const { title } = useContext(HeadTitleContext);
if (!title) return null;
return <title>{title}</title>;
};Then, I integrated the context provider and HeadTitle component within the layout. src/app/layout.tsx import { HeadTitle, HeadTitleProvider } from "@/components/head-title";
export { metadata } from "./metadata";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<HeadTitleProvider>
<html lang="en">
<head>
<HeadTitle />
</head>
<body>{children}</body>
</html>
</HeadTitleProvider>
);
}With this setup, the HeadTitle component displays the title only if it’s set in the context, preserving default metadata or generateMetadata settings otherwise. This approach can be extended to other meta tags dynamically. Example usage: "use client";
import { useHeadTitle } from "@/components/head-title";
import { useTimer } from "@/components/timer/use-timer";
import { useEffect } from "react";
export const HeadTitleController = () => {
const { setTitle } = useHeadTitle();
const { time, status } = useTimer();
useEffect(() => {
if (status === "running") {
setTitle(`${time.mm}:${time.ss} - Web Timer`);
} else {
setTitle("");
}
}, [status, time.mm, time.ss, setTitle]);
return null;
};Resetting to the default title is as simple as calling setTitle(""). |
|
A common pattern is to see some realtime data in the title, such as a notification count. How can this be done in Next without removing the main title from the exported metadata object? Ideally we would have a static title for SEO that is updatable on the client. For example, let's say you have "Page Title" as your title for search results, but you want "(NotificationCount) - Page Title" on the same route for logged in users. I can render a There doesn't seem to be any way to get the updated title to override that original title tag. I can overwrite document.title in an effect, but it appears for a split second and then reverts back to the title from the exported metadata. |
|
This is still not resolved? 😐 |
|
WTH! Still not resolved? |
|
WTH! Still not resolved? |
|
I managed to fix it, but...🤔 |
|
I thought about this but wanted clean version, so created new file and
rendering the client component there calling my title, description from
constant file. (it worked for me)
…On Thu, 25 Sept 2025 at 08:42, UTSUMI Keiji ***@***.***> wrote:
I managed to fix it, but...🤔
useEffect(() => {
document.title = newTitle;
// HACK: Workaround for cases where updates are not applied on reload
setTimeout(() => {
document.title = newTitle;
}, 100);
}, []);
—
Reply to this email directly, view it on GitHub
<#50872 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BOCBV4GX7CAXFIQKRMCECDD3UNMRXAVCNFSM6AAAAAAY47QJTWVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTINJQGU2DEMI>
.
You are receiving this because you commented.Message ID: <vercel/next.
***@***.***>
|
Fix: Dynamic Page Titles with Client Components in Next.js App RouterSolution for setting dynamic page titles when using "next": "^15.5.2"Problem'use client'
export const metadata = { title: 'About' }Error: SolutionAdd a server layout wrapper for each client component page. Step 1: Create Metadata Helper
import type { Metadata } from 'next'
interface PageMetadata {
title: string
description?: string
}
export function createMetadata({ title, description }: PageMetadata): Metadata {
return {
title: `${title} | MyApp`,
description: description || 'Default description',
}
}Step 2: Add Layout Wrapper
import AboutPage from "./page"
import { createMetadata } from '@/lib/metadata'
export const metadata = createMetadata({
title: 'About Us',
description: 'Learn more about us'
})
export default function AboutLayout() {
return <AboutPage />
}Step 3: Keep Client Component
'use client'
export default function AboutPage() {
return <div>About</div>
}Directory StructureHow It Works
Result✅ Server-side metadata for SEO |
|
With next/navigation I was able to parse and format the pathname and placed it in a useEffect, so it updates automatically on change. Are there any downsides to this method other than the generic path value? |
|
I don't believe in bashing work, but it makes ZERO sense to have to go online to look for solutions for just a ttile tag that can be added as simply as <title>Title</title> |
|
Since this is still a thing in 2026, taking inspirations from Rom-888's answer in SO and chemiadel's answer, I came up with this solution using a custom hook: "use client";
import { useEffect, useRef } from "react";
export function useTitle(title: string) {
const titleRef = useRef(title);
useEffect(() => {
titleRef.current = title;
document.title = titleRef.current;
const observer = new MutationObserver(() => {
if (document.title !== titleRef.current) {
document.title = titleRef.current;
}
});
observer.observe(document.head, {
subtree: true,
characterData: true,
childList: true
});
return () => observer.disconnect();
}, [title]);
}Using just |
Yeah, refactoring seems be the way to go. Keep the page a server component, add the metadata there, and import the client component to it. Makes you miss how flexible the Head component was, but at the same time, it sort of makes sense to separate these... at least if the general design pattern is to push client components to the leaves of the React tree.
An update on this:
The Metadata API exists to help Next.js manage and compose the metadata for a route. However, React itself also supports rendering document metadata, so yes — you can totally inline:
in your JSX. React will hoist it into the document
<head>, and this works from a Client Component too.So for …