Open Josehower opened 1 year ago
Interesting, seems like Shu was able to get a document.title
on this example page
This was part of this PR:
Not sure how this was achieved, I've asked over here:
I was actually having a different problem to this one just now - the metadata
from the root layout is also not currently (as of next@13.2.5-canary.20
) applied to not-found.tsx
404 pages (maybe also error.tsx
?) 😯
I guess for a static title, this hack can be used with the App Router (doesn't result in a hydration error, interestingly enough):
// app/not-found.tsx
export default function NotFound() {
return (
<div>
{/*
No support for metadata in not-found.tsx yet
https://github.com/vercel/next.js/pull/47328#issuecomment-1488891093
*/}
<title>Not Found | example.com</title>
</div>
);
}
Maybe this is what it will look like in the future once React Float more fully lands...
Or @gnoff @sebmarkbage should this already work now with the current state of React Float + Next.js? Was really surprised by lack of hydration error in both dev and production 🤔
Update Feb 2024: Maybe this is actually the new Document Metadata feature mentioned in the React Labs Feb 2024 "What We've Been Working On" blog post:
Document Metadata: we added built-in support for rendering
<title>
,<meta>
, and metadata<link>
tags anywhere in your component tree. These work the same way in all environments, including fully client-side code, SSR, and RSC. This provides built-in support for features pioneered by libraries like React Helmet.
We're thinking to support it but might only support metadata
as you could keep use notFound()
use inside generateMetadata()
which could cause more issues
but might only support
metadata
as you could keep usenotFound()
use insidegenerateMetadata()
which could cause more issues
Oh too bad, so no way to generate dynamic titles / descriptions based on the requested content 🙁
Maybe there would still be some ways to support generateMetadata()
eg:
notFound()
used within generateMetadata()
in not-found.tsx
/ error.tsx
, throw an error@karlhorky error checking is possible to be added there to avoid using notFound
in error/not-found page convention. Just curious, mind sharing the dynamic title/description case for your not-found page?
Sure, I was thinking of something kind of similar to the one in the issue description:
// app/animals/[animalId]/not-found.js
export async function generateMetadata(props) {
return {
title: `Animal id ${props.params.animalId} not found`,
description: `There is no animal with id "${props.params.animalId}"`,
};
}
export default function AnimalNotFound(props) {
return <div>Sorry, an animal with id "{props.params.animalId}" was not found</div>;
}
I see, dynamic routes with params makes sense. We'll think of support for that case
I'm also hoping this feature is added soon. We were able to do this in the pages
directory using <Head>
, so it'd be nice to have it with the app
directory too 😄
Would love this feature as well. Running a i18n site with this folder structure -> [locale]/not-found.tsx
I want to be able to get the locale param so we can fetch correct translations for not-found page.
A related issue I don't see mentioned here is that without metadata in error pages, the viewport
meta tag is missing and mobile layouts are broken.
I have something working for me. I determine if I am in a 404 state in generateMetadata for a route like. [blah]/page.tsx and return something like { title: 'Not Found' } in [blah]/not-found.tsx I just have the error markup. The meta data from page.tsx is present when I get my 404 hit I get the metadata set in page.tsx + added by the notFound() call. (notFound() is called in page.tsx's component tree, not in generateMetadata of [blah]/page.tsx
Currently, I need to manually add <meta name="viewport" content="width=device-width, initial-scale=1" ></meta>
into the not found page in order to make it responsive
@huozhi Any ETA on when params
support will be added to the not-found.js
page? I find it hard to believe that Next doesn't support localized 404 pages out of the box, that seems like a pretty important feature.
Currently, I need to manually add
<meta name="viewport" content="width=device-width, initial-scale=1" ></meta>
into the not found page in order to make it responsive
@bacvietswiss that issue is fixed in #50044
@leonbe02 Can you file another issue about that as the original issue here is related to metadata support
@leonbe02 Can you file another issue about that as the original issue here is related to metadata support
I found an existing ticket that outlines the issue: https://github.com/vercel/next.js/issues/48763
@huozhi The not found page is refreshing every 3 seconds and we don't have <link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="any">
tag.
@sayhicoelho have you ever fixed the refreshing thing?
@sayhicoelho have you ever fixed the refreshing thing?
No. I give up Next for now. I had a lot of problems and a lot of weird bugs.
Regarding the custom metadata for the not-found.js page, maybe I've found a workaround!
Currently, we are facing a problem where we throw a notFound() error when no page is found in our dynamic route. At that time, we don't see the meta title and description of the not-found.js page. Instead, we see the meta data of the current page. I believe this is logical and not a bug in Next.js. In the case of a dynamic route, the meta data of the not-found.js page should also be dynamic, as it might be useful in some scenarios.
So, in the case of a dynamic route, we can set the meta data for the not-found.js page dynamically from page.js. It's important to note that the generateMetadata function should not throw a notFound error. Instead, the page component should throw a notFound error. For example, you can see the screenshot below. I hope this solution works.
My folder structure:
app/[locale]/[...slugs]/page.js
app/[locale]/[...slugs]/not-found.js
app/[locale]/[...slugs]/error.js
@MuhammadMinhaj nice, but the workaround still doesn't solve de problem for the catch-all unmatched routes
@itsjavi One possible workaround for that is similar to https://github.com/vercel/next.js/issues/48763#issuecomment-1623745516
...you can use a catch all route and put a page.js
under it, and then populate generateMetadata
.
A possibly simpler interim solution to keep everything in not-found.js
may be to just update the title in the client side DOM e.g.
useEffect(() => document.title = "Page Not Found")
...as I suppose for error pages other meta tags are probably less important and don't really need SSR support.
However, I agree with this issue that error pages should have their own generateMetadata
method, as it would be a cleaner and more consistent.
Did some research around metadata support for not-found
and error
convention. It's possible to support in not-found
when it's server components as currently metadata is only available but for error
it has to be client components then it's hard to support it there as it's a boundary. To support that it requires more changes in next.js to make them possible.
Should generateMetadata
work in not-found
file then? I'm testing in v13.4.19
and it's nothing.
Should
generateMetadata
work innot-found
file then? I'm testing inv13.4.19
and it's nothing.
It only doesn’t work during development, if you make a build, the metadata is displayed correctly. I also noticed that if you specify robots, it is duplicated on the page 2 times. Initially, next adds robots to the page with the value noindex.
Should
generateMetadata
work innot-found
file then? I'm testing inv13.4.19
and it's nothing.It only doesn’t work during development, if you make a build, the metadata is displayed correctly. I also noticed that if you specify robots, it is duplicated on the page 2 times. Initially, next adds robots to the page with the value noindex.
Nope.. at least for me, I'm testing 14.0 and my issue is still there. I have title and description defined statically in a parent layout file, and then in a child route an error file that simply tries to redefine them statically. When the error page loads, it shows for a fraction of second the correct metadata, and then it reverts back to the layout (parent) one. This happens in both dev and prod builds.. I agree that Next has a ton of weird issues in apparently basic features, it makes me miss the good old client-server model...
Just like @karlhorky mentioned
<div>
<title>Not Found</title>
</div>
works. But I am using template
export const metadata: Metadata = {
title: {
template: '%s | My Website Name',
default: 'My Website Name'
}
So for someone like me, I had to write the below code
<div>
<title>Not Found | My Website Name</title>
</div>
The
Next.js 14.0.4 using App Router and not-found.tsx
:
export const metadata = {
title: 'Page not found',
description: 'This page could not be found',
};
It works when I load the page, briefly, but the page.tsx
in the same folder overwrites the title immediately.
Tested in developer-mode. Obviously, that should not happen.
Reading the docs: https://nextjs.org/docs/app/building-your-application/optimizing/metadata
...also doesn't clear up the issue. Pages like not-found.tsx
aren't part of the hierarchy, I suppose.
Meanwhile, to solve the issue of page titles on 404-pages, assuming you have:
Then your not-found metadata currently cannot come from your not-found.tsx file, instead, you are to put it into your page.tsx file:
type Params = {
id: string;
};
const metadata: Metadata = {
title: 'My page',
description: 'Viewing a single page',
};
export async function generateMetadata({
params,
}: {
params: Params;
}): Promise<Metadata> {
const exists = await pageExists(Number(params.id));
if (!exists) {
return {
title: 'Page not found',
description: 'This page could not be found.',
};
}
return metadata;
}
export default async function Page({ params }: { params: Params }) {
const exists = await pageExists(Number(params.id));
if (!exists) {
notFound();
}
return (
<p>
Your page goes here.
</p>
);
}
My issue with this is that I want the not-found page to be exclusively responsible for deciding how a 404-situation would look and feel. Now I need to make two files responsible for it, and I even need to do pageExists
in both the page component and the generateMetadata
function.
But at least it works :)
It works when I load the page, briefly, but the page.tsx in the same folder overwrites the title immediately.
I'm running into this issue now too, and it feels very unintuitive. I'd expect that when I call notFound()
in a page, it's a signal to Next that this page should appear as nonexistent, and the entirety of my not-found.tsx
route should render, metadata included.
Having the underlying page title show up breaks that experience. Suppose I have a dynamic page that fetches some resource and checks to see if the current user should be able to view it. If they shouldn't, then I want to tell them that the resource doesn't exist by rendering our 404 page.
If the name of the resource shows up in the page title, it feels like a leaking of information that the current user shouldn't have access to. Of course, I can do the additional check in the page's generateMetadata
function as @marcelhageman suggests above, but that's definitely a hacky workaround to this unexpected behaviour.
Edit: I figured out that I can get the not-found.tsx
UI and metadata to render correctly by calling notFound()
from within my generateMetadata
function.
export const generateMetadata = async ({ params }: { params: id }): Promise<Metadata> => {
const thing = await getThing();
if (!thing) {
notFound();
}
const { user } = await getCurrentUser();
if (!user || !hasAccess(user, thing)) {
notFound();
}
return {
title: `Custom title for ${thing.name}`
}
}
However this also feels like a hack and unexpected behaviour. Just wanted to mention this here in case others need a workaround while this issue is being addressed.
Any update so far?
Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!
This is my usecase and some notes on it:
I have:
/app/not-found.tsx
/app/blog/[...slug]/page.tsx
When visiting: http://localhost:3000/blog/non-existant-page
The /app/blog/[...slug]/page.tsx
route fires as expected.
/app/blog/[...slug]/page.tsx
export const generateMetadata = async ({ params }: { params: { slug: string[] } }): Promise<Metadata> => {
const post = await getPostFromParams({ params });
if (!post) {
console.warn('Failed to find post during generateMetadata.');
return {
title: 'fallback';
};
}
return {
title: 'Blog!';
}
}
const BlogPostHandler = async ({ params }: { params: { slug: string[] } }) => {
const post = await getPostFromParams({ params });
if (!post) {
notFound();
return null;
}
//other logic
}
not-found.tsx:
import NotFoundPage from '@/containers/NotFoundPage';
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Not Found',
};
const NotFound = () => <NotFoundPage />;
export default NotFound;
The behaviour I observe is:
Not Found
which is the root layouts title (this is also what is present in the initial HTML)fallback
which is what the page.tsx
resolves to.Now if both your page.tsx
and not-found.tsx
page have the same meta data (i.e. static metadata object you return in both for the not found case) then this works pretty well. If not, there will be a change from either the layout meta data or not-found.tsx
metadata to whatever the page.tsx
metadata is.
PS: You should probably use notFound()
from inside the generateMetadata
.
It would be nice if we could export metadata
as null or return null
for generateMetadata
to tell next
to not use this metadata and fallback to the previously available meta data.
Is it possible to set metadata for error.tsx file on SSR? I cannot find any information, generateMetadata function also does not work for client components (error.tsx forced to be client component).
Is it possible to set metadata for error.tsx file on SSR? I cannot find any information, generateMetadata function also does not work for client components (error.tsx forced to be client component).
error.tsx is client only page, for now, there's no possible way to set metadata or even use generateMetaData to error.tsx page
But you can use a hack like this
@Super-Kenil Thanks for reply My goal was to keep at least default meta tags for error page in case if it is exposed to search engines. However, I noticed that error page has meta robots tag with "noindex" value, so the problem itself seems not actual from the beginning.
It is logical that page with error must not be indexed, but still it is strange that there is no way to handle error page's meta tags on server side. All depends on project needs, it is not ok to just to remove such possibility at all.
Describe the feature you'd like to request
I would like to have the possibility to control the page title, description etc. in the error and not-found page.
currently Its possible to get around not-found page by creating a conditional in metadata function for the not-found page but is still not nice.
Describe the solution you'd like
I would like not-found.js and error.js accept
metadata
andgenerateMetadata()
similar topage.js
andlayout.js
Describe alternatives you've considered
for not-found, maybe allow to pass the metadata object as an argument for the function