Next.js Discord

Discord Forum

Dynamic Page Titles using App Router

Unanswered
Yacare Caiman posted this in #help-forum
Open in Discord
Yacare CaimanOP
Hi there - I'm trying to set a page title within the nested page.tsx files but have that value passed down to a <Header> component contained within the route group's layout so it's dynamic displayed visually across all different pages. I'm using the app router, my understanding is this getServerSideProps / getStaticProps no longer apply? How (or) is this possible without setting it via a context / provider and wrapping everything?

50 Replies

you instead use the "generateMetadata" function, which can be asynchronous
so the data you fetched in "getServerSideProps", can be instead fetched in generateMetadata
if you happen to need the call in your page too, that's ok to call twice: fetch will auto deduplicate, and you can use "cache" for other methods of fetching data
I think that it's a separate function because on certain type of calls, only generateMetadata will be called (eg a HEAD request that gets metadata of your site but not the content)
Yacare CaimanOP
Hi Eric, thank you for your help - I'll check that out... I'm new to next.js/react in general but all the examples I saw referenced getServerSideProps but I'm using app router and reading the next docs I can see this no longer applies to app router
Yacare CaimanOP
Hmm I'm not sure that will work with metadata as that's dynamically settings the <head>, I'm trying to set a val for which will get passed into a layout header component for visual rendering, e.g. <Header title={title}> but setting that val from within the page (that could also be different to the <head><title>val</title></head> value
you can remove the Header thing
instead the returned object from generateMetadata will be the values in the header
it uses a typed JSON
instead of HTML
Yacare CaimanOP
Two secs, let me post an example
but will be transformed to HTML
Yacare CaimanOP
function Header({ title }: { title?: string }) {
  const { sidebarOpen } = useSidebar();
  return (
    <header className="sticky top-0 z-999 flex w-full bg-white">
      <div className="flex flex-grow items-center justify-between px-4 py-4 md:px-6 2xl:px-11">
        <h1 className=" text-title-xl2">{title}</h1>
        <div className="flex items-center gap-2 sm:gap-4 lg:hidden">
          <>{sidebarOpen}</>
          <SidebarButtonToggle
            placement={"header"}
            size={"icon"}
            variant={"toggle"}
          />
        </div>
      </div>
    </header>
  );
}
ah ok by dynamic you mean more "interactive", in the sense cleint-side interfactive
so for this case, you can just alter the thing programmatically
eg "onClick(() => { document.title = "yo"}
if you need some data to do so, fetch them in the page RSC
Yacare CaimanOP
It's actually my header component, which is included in the route group layout
damn I am so sorry
Yacare CaimanOP
But for nested pages I want to set the title which will be rendered out to the page visually (in the header component)
since the beginning, I am conflating your header with "head" haha
Yacare CaimanOP
Haha I know sorry - it's confusing because the terms are ambiguous 🤣
let me start again
is this getServerSideProps / getStaticProps no longer apply? How (or) is this possible without setting it via a context / provider and wrapping everything?
=> in Next 13 you "just" get your data
beceause React Server Components can be async
you can make your page a RSC, get the data, and render whatever you want
you can indeed set a client context from a layout RSC typically
it's also possible to do that at component level, you can craft an async component "HeaderAsync" that gets data and pass the value to Header. Then you can wrap HeaderAsync within a suspense to do component level data fetching
Yacare CaimanOP
But in here for example:

app/(dashboard)/settings/services/page.tsx

import PageTitle from "@/app/_components/PageTitle";

export default function Page() {
  return (
    <>
      <PageTitle
        title="Services"
        description="Manage your connected Services"
      />
      <h2></h2>
    </>
  );
}


Rather than including a PageTitle component within each page, I'd prefer to have this called in within a common <Header> component within the layout and just set some vars in the page.tsx
OK yea I thought the context/provider model was the approach, just wondered if there was something 'easier' as it feels like overkill
you can craft a PageTitleAsync component
function async PageTitleAsync() {
   // get your title here
   const data = await someData()
   return <PageTitleClient data={data} />
}
export function PageTitle()  {
    return <Suspense><PageTitleAsync</Suspense>
}
but if you are using getServerSideProps, it's less convoluted to just turn this code into an async page
it's exactly the same pattern
but translated to the App Router
Yacare CaimanOP
The page title isn't dynamic in the sense it needs calling from elsewhere though, think of the hierarchy like so:

- layout.tsx <-- includes a <Header> component with a common <PageTitle> element
- (group)
-- section
--- page.tsx <-- want to set a title which the layout will render via <Header> and <PageTitle>
yo need to move the header part in pages and rerender it
this kinda defeat the purpose of layouts, but in Next they are not rerendered on route change (contrary to what other frameworks mean by layout)
you can use a template instead
it's rerendered
and better than moving the layout to each page
Yacare CaimanOP
Yea moving layout to each page is exactly what I wanted to avoid - cool, thanks I'll check that out in a mo. Head buried in code building other bits at the moment 😄