next.js: Metadata: title template in layout.js doesn't work for page.js in the same level
Verify canary release
- I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: darwin
Arch: x64
Version: Darwin Kernel Version 22.3.0: Mon Jan 30 20:42:11 PST 2023; root:xnu-8792.81.3~2/RELEASE_X86_64
Binaries:
Node: 18.14.0
npm: 9.3.1
Yarn: 1.22.19
pnpm: 7.29.0
Relevant packages:
next: 13.2.4-canary.4
eslint-config-next: N/A
react: 18.2.0
react-dom: 18.2.0
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true), Metadata (metadata, generateMetadata, next/head, head.js)
Link to the code that reproduces this issue
https://github.com/joulev/debug/tree/nextjs-metadata-title-template-bug
To Reproduce
- Clone the linked repo
next dev
ornext build
- Check
/
and/about
Alternatively, check the deployment for /
and /about
.
Describe the Bug
/about
title is “about | template” which is correct. But /
title is “home” without the " | template" part, which is not correct.
Expected Behavior
/
title should be “home | template”.
Which browser are you using? (if relevant)
N/A
How are you deploying your application? (if relevant)
N/A
About this issue
- Original URL
- State: closed
- Created a year ago
- Reactions: 1
- Comments: 42 (29 by maintainers)
Thanks @gnoff for your time in answering. The description you’ve given is really clear.
In the first part, you are right around the title rendering - it probably should have had ‘’ defined for it to render a title.
To be honest, the above was probably overkill - and I do understand that the title and template gets cascaded down until overwritten. The main point I was trying to make is that I believe it would make more sense for the template to define from the current level downwards, and that all the use cases would still be achievable- but with a better DX for the reasons I mentioned above (colocation of title/template, alignment of the template with the actual layout, etc).
I did have to reread the part about templateForChildTitles a few times to be honest, because I was still confused about the fact that the template didn’t apply to the child /products/explore/page.js. but the default title did.
I understand your logic, and it’s probably just a matter of a difference of opinion on what the best developer experience would be at this stage.
Thanks for the hard work.
@mjth
I don’t quite follow this part. this metadata doesn’t declare a title, why does it use a template? Also wouldn’t the computed title have an extra space if you did treat the title to be templatized as an empty string?
In trying to understand your examples though it made me realize that you might be thinking about how we “find” metadata for a page in a reverse order of sorts. My impression is you think about it as starting from the Page and looking up the Layout tree to find matching metadata whereas it actually starts with the root layout and computes a final value as it goes down. I’ll try to clarify my mental model for the titles and see if this helps.
I think our Docs need to better clarify our intent here (or we need to make these properties longer) but here is how I think about the options
In a Layout
title defines default titles to use as well as templates to be applied to deeper title definitions
these titles are always fallbacks for when you do not configure a title for your page. As we traverse each layout we keep track of the default title, augmenting it with templates as needed. We also keep track of whether a new template is to be used for deeper title definitions
In a Page
title defines the page title. If you don’t provide a title the nearest default title is used
It’s important to note that a template never does anything on it’s own. It always requires a title to augment (either a default title defined for a Layout or a title defined for a page)
The type structure could make this distinction more explicit
But generally we have a philosophy that it metadata is changing together it should be grouped together. This is why the template is embedded in the title definition and not a sibling property. We also try to communicate enough intent without using long property names.
Another thing that might help the mental model is to recognize that we don’t start with the Page and bubble up to find the first matching title/template. We start at the root Layout and compute the title as we go deeper. If we never hit a Page that defines title then we just use the currently computed title. This is why we haven’t really “skipped” the template. The template is just there to augment deeper definitions if one exists. sometimes one will not exist
I think we need to do some Docs updates to make this mental model show through clearer, but I do think our position is consistent and does allow for the greatest range of expression of titles.
If you want the behavior where the default title does not inherit the locally defined template you can do
And if you want the template to apply to the default you can easily do that
The most awkward case I can see is when you want your Page to use the template defined in your Layout but the same absolute trick works there too, it’s just in a separate file.
@huozhi, I’m not sure if I understand correctly, but I don’t think giving another title in the root page is a good sollution. If you want to have a behavior like this:
Then you’d have to manually write ‘Home | Brand’ in the metadata title in
app/page.tsx
like so:Which ultimately kind of defeats the purpose of title templates.
I don’t see any way you could use the current behavior to your advantage and even if there is one I think it’s safe to assume that there would be significantly more people wanting to use the title template in the root page (if they wanted to have just the default title on the root page, they could achieve it by just not specifying a title in the
app/page.tsx
metadata)Also, as @mjth said, this is really bad DX-wise. Even if we introduce a change in the docs, it would still be really inconstant with the layout working on pages on the same level but the metadata not.
In my opinion the current behavior is really unintuitive and not really beneficial for anyone so I don’t think a change in the documentation is the right way to approach this issue. And even if there would still be any need to use the current behavior, we could make it opt-in with a boolean, as @JesseKoldewijn suggested. It could look something like this:
That way you could also opt-out of templates in lower level files and the whole thing would be much more intuitive. (And as I mentioned before, you could still achieve the default “Brand” behavior by not specifying a title in a page’s metadata)
I agree with @Confuze… and hope this issue is opened again @huozhi
I was attempting to specify a title template in Root Layout, but it doesn’t apply to the Root Page (and there is no way to do this). If this was designed in case you needed an escape hatch, you could either just not define the template on root, or override it (like you would for layouts further down the chain anyway…)
IMO it’s also very confusing from a DX perspective… The layout is designed to wrap the page on the same level… but the metadata only wraps children pages?
Yeah I’m just as “Confuzed” about this matter myself as well
@Confuze @JesseKoldewijn
We try to keep our boolean config as limited as possible which is why the current set of configurable options is descriptive (absolute is a description of the absolute title rather than a flag like
skipTemplate: true
)It may seem a little odd (b/c the title maybe seems like a minor reason to do this) but you can hook into the template behavior you want by structuring the directory slightly differently. route groups are the way to do this
now your Page is in a “deeper” segment than the Layout. It’s a little nuanced because earlier I said route-segment and really its based on folder hierarchy but usually these are the same thing. But in this case the page is part of a route-group which has no url representation but when the metadata resolves it is treated like it is a “child” of the Layout
that said, I personally wouldn’t do this, I’d just use a shared helper to make the template uniform when I wanted this behavior
btw, this is exactly what I meant. @JesseKoldewijn posted this comment while I was writing mine lol.
So would in this case maybe a override boolean be possible that defaults to the current behaviour? This way you could have it both ways. This boolean as confuze layed out in a snippet could be included into the meta object as an optional override.
@Confuze 's snippet
bro I became your name again, looking all over the place like “am I blind?” 🤣
I btw mainly meant the config option as in an option in the metadata object btw. Just to clarify🤙
@joulev If you don’t specify title for root page then it will use the default title from root layout, it will render
"Brand"
if you don’t specify any.Ok this is indeed the desired behaviour (#45965), but I think I’ll keep this issue open as I still don’t understand the rationale behind this.
Also I think it’s a good idea to update the documentation to reflect this, especially as the Title templates section uses an example where
app/page.js
and a same-levelapp/layout.js
are used, which makes it quite misleading.After digging a bit I found this
https://github.com/vercel/next.js/blob/d59aa9655e7a46056a4e98c3a79d40a118f6157d/packages/next/src/lib/metadata/resolve-metadata.ts#L317-L325
So it does look like this is the desired behaviour… but may I ask why?
Simply changing it to
fixes the issue.