dev3 ·
I need per-page metadata (title, description, canonical, og:image) for blog posts loaded from a database. What is the right API in the App Router and how do I avoid fetching twice?
0
DEV3 Developer Forum
Every post is reviewed first. Usually reviewed within 24 hours.
I need per-page metadata (title, description, canonical, og:image) for blog posts loaded from a database. What is the right API in the App Router and how do I avoid fetching twice?
Export generateMetadata from the page. Next merges it with parent layouts.
// app/blog/[slug]/page.tsx
import type { Metadata } from "next";
import { cache } from "react";
import { notFound } from "next/navigation";
const getPost = cache(async (slug: string) => db.post.findUnique({ where: { slug } }));
export async function generateMetadata(
{ params }: { params: Promise<{ slug: string }> }
): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
if (!post) return {};
return {
title: post.title,
description: post.excerpt,
alternates: { canonical: `/blog/${slug}` },
openGraph: {
title: post.title,
description: post.excerpt,
type: "article",
images: [{ url: post.cover, width: 1200, height: 630 }],
},
twitter: { card: "summary_large_image" },
};
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const post = await getPost((await params).slug);
if (!post) notFound();
return <article>{post.title}</article>;
}
Wrap the loader in React cache() (as above) so generateMetadata and the page share one query per request. Plain fetch calls are memoised automatically.
Set metadataBase once in the root layout so relative URLs resolve:
export const metadata: Metadata = {
metadataBase: new URL("https://example.com"),
title: { default: "Example", template: "%s | Example" },
};
app/sitemap.ts and app/robots.ts generate those files.app/opengraph-image.tsx renders OG images with ImageResponse.JSON.stringify(data) in the page.Pitfall: notFound() inside generateMetadata is fine, but returning metadata for a missing record produces soft-404 pages.
Preview the result with the meta tag preview builder.