How to display page title in layout with app router? #59532
-
SummaryI've searched the docs, asked the Goggle, have found many similar questions, but no appropriate answer. It looks like I'm missing some major concept of the new app router. Currently I have one layout and lot's of pages. Layout contains header, footer, sidebar, etc. Somewhere deep in the header there is a SomePage.getLayout = function getLayout(page) {
return (
<MainLayout title="Page title">
{page}
</MainLayout>
)
}as well as for dynamic routes: DynamicPage.getLayout = function getLayout(page) {
return (
<MainLayout title={page.props.title}>
{page}
</MainLayout>
)
}But I couldn't find any clue how to implement it with the app router. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 5 replies
-
|
Layout is just a common UI that is shared across pages. Also Read more; https://nextjs.org/docs/app/api-reference/file-conventions/layout |
Beta Was this translation helpful? Give feedback.
-
|
after using metadata to set the title for each page, you can access your page title in client side layout or other components with for passing undefined document error we should use it after rendering the dom with the below trick: |
Beta Was this translation helpful? Give feedback.
-
|
@devbymak solution works perfectly when metadata is statically defined. But when "use client";
import { useEffect, useMemo, useState } from "react";
// This is optional and can be omitted
export type TitleObserverCallback = (title: string) => void;
export function useTitleObserver(onTitleChange?: TitleObserverCallback) {
const [title, setTitle] = useState("");
const isMounted = useMemo(() => true, []);
useEffect(() => {
const observer = new MutationObserver((mutations) => {
let newTitle = undefined;
mutations.forEach((mutation) => {
if (mutation.target === document.head && mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.nodeName === "TITLE")
newTitle = node.textContent;
})
}
if (mutation.target.nodeName === "TITLE")
newTitle = mutation.target.textContent;
});
if (newTitle !== undefined)
setTitle(newTitle);
});
// <head> is observed because Next.js does not change title contents but removes and adds <title> node
observer.observe(document.head, {
subtree: true,
characterData: true,
childList: true,
});
return () => observer.disconnect();
}, []);
// As mentioned earlier this is optional and can be omitted
useEffect(() => {
if (title !== "" && onTitleChange)
onTitleChange(title);
}, [onTitleChange, title]);
// Return dynamically set title
if (title !== "")
return title;
// Fallback to static title
return isMounted ? document.title : "";
} |
Beta Was this translation helpful? Give feedback.
@devbymak solution works perfectly when metadata is statically defined. But when
generateMetadata()is used, title is not set. This is because first render is done before dynamic metadata is applied. So I had to implement MutationObserver hook: