Docs: generateMetadata - can we generate its output statically? Docs don't mention this.
What is the documentation issue?
I can't find in the docs of generateMetadata, whether its output can be generated statically, provided the page for which it is for, is also statically generated.
Assume we have dynamic page (app/blog/[slug]/page.js) which contains inside this:
// This is inside the app/blog/[slug]/page.js
export async function generateStaticParams() {
let posts = getBlogPosts()?.posts;
return posts.map((post) => ({
slug: post.slug,
}));
}
Now, assume that same page also contains this:
// This is also inside the app/blog/[slug]/page.js
export async function generateMetadata({ params }) {
let post = getBlogPosts()?.posts?.find((post) => post.slug === params.slug);
if (!post) {
return;
}
let { title, publishedAt: publishedTime, summary: description, image } = post.metadata;
let ogImage = image ? image : `${baseUrl}/og?title=${encodeURIComponent(title)}`;
return {
title,
description,
openGraph: {
title,
description,
type: "article",
publishedTime,
url: `${baseUrl}/blog/${post.slug}`,
images: [
{
url: ogImage,
},
],
},
twitter: {
card: "summary_large_image",
title,
description,
images: [ogImage],
},
};
}
Does the output of generateMetadata also generate statically in this case (similarly to the page itself)?
The documentation should mention this. I can't find such information.
Is there any context that might help us understand?
N/A
Does the docs page already exist? Please link to it.
https://nextjs.org/docs/app/api-reference/functions/generate-metadata
Hi,
Yes, but it depends...
Let's take a concrete actionable example:
[!NOTE]
https://httpbin.dev/uuid returns a different uuid on every invokation
// app/posts/[slug]/page.tsx
export async function generateStaticParams() {
return ['a', 'b', 'c'].map((post) => ({
slug: post,
}));
}
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const { uuid } = await fetch("https://httpbin.dev/uuid").then(res => res.json())
return {
title: slug,
description: uuid,
};
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
return <h1>{slug}</h1>
}
When I build, I will end up with these static files:
➜ tree .next/server/app/posts
.next/server/app/posts
├── [slug]
│ ├── page.js
│ ├── page.js.nft.json
│ └── page_client-reference-manifest.js
├── a.html
├── b.html
├── c.html
└── ...other files
For sake of this conversation I've removed other files you might see.
If I inspect the .html files, I see my metadata. And when I run the app, and visit these pages, the description is a stable, never changing uuid.
However, if we change the generateMetadata function to:
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const { uuid } = await fetch("https://httpbin.dev/uuid", { cache: 'no-cache' }).then(res => res.json())
return {
title: slug,
description: uuid,
};
}
Now I build and tree:
➜ tree .next/server/app/posts
.next/server/app/posts
└── [slug]
├── page.js
├── page.js.nft.json
└── page_client-reference-manifest.js
I've accidentally opted out from static rendering.
You can also use genererateMetadata with export mode too.
I'll see to add some clarification to the generateMetadata API reference though. It can be included in static outputs, given that the page itself doesn't get opt-into dynamic/runtime rendering.
We've document this now. Thanks for the feedback. I still think there's room to improve around generateMetadata. I've gotten another clarification request. I'll review the whole thing and maybe some more additions come out of that.
We've document this now. Thanks for the feedback. I still think there's room to improve around generateMetadata. I've gotten another clarification request. I'll review the whole thing and maybe some more additions come out of that.
You're welcome. IMHO there is room for improvement for other pages too. I will try to open issue when I stumble something - or maybe use the "Feedback" button on the site, if that is monitored.
Yes, we monitor that channel, but I'd prefer for this kind of issues - either a discussions topic, https://github.com/vercel/next.js/discussions, or for more actionable feedback, an issue.
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.