While searching how to add metadata for search engine optimization, I came across a post on velog by Doeunnkimm and had been injecting metadata statically.
Once I started posting, I wanted to add different metadata for each post in dynamic routing.
So I’m writing this as a review and reference.
1. Code
First, the code I got from the velog post above is as follows.
// First, set the basic metadata you need.
const META = {
title: "Blog title",
siteName: "Site name",
description: "A brief description of the blog",
keyword: ["array", "type", "keywords"],
url: "https://sample.kr",
googleVerification: "xxx", // Google Search Console
naverVerification: "xxx", // Naver Search Advisor
ogImage: "https://sample.kr/assets/thumbnail.png", // thumbnail,
} as const;
// Use the getMetadata function to set metadata.
// With this single function, you can cover most metadata needed for most sites.
export const getMetadata = (metadataProps?: generateMetadataProps) => {
const { title, description, asPath, ogImage } = metadataProps || {};
const TITLE = title ? `${title} | Blog title` : META.title;
const DESCRIPTION = description || META.description;
const PAGE_URL = asPath ? asPath : "";
const OG_IMAGE = ogImage || META.ogImage;
const metadata: Metadata = {
metadataBase: new URL(META.url),
alternates: {
canonical: PAGE_URL,
},
title: TITLE,
description: DESCRIPTION,
keywords: [...META.keyword],
openGraph: {
title: TITLE,
description: DESCRIPTION,
siteName: TITLE,
locale: "ko_KR",
type: "website",
url: PAGE_URL,
images: {
url: OG_IMAGE,
},
},
verification: {
google: META.googleVerification,
other: {
"naver-site-verification": META.naverVerification,
},
},
twitter: {
title: TITLE,
description: DESCRIPTION,
images: {
url: OG_IMAGE,
},
},
icons: {
icon: "/favicon/favicon.ico",
},
};
return metadata;
};
Then you call this code in layout.tsx to use it.
By creating the metadata and exporting it, you generate metadata for the homepage.
import "./globals.css";
import { Metadata } from "next";
import { getMetadata } from "@/utils/getMetadata";
// Export the metadata object
export const metadata: Metadata = {
// Use the spread operator .., to bring in the object.
...getMetadata(),
// If you want to use another title, you can do it like this.
...getMetadata({ title: "Another title" }),
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko">
<body className="h-svh">
<div>
<div className="mx-auto max-w-7xl p-6 lg:px-8">{children}</div>
</div>
</body>
</html>
);
}
Open your homepage, press the F12 key, and check the <head> tag to see the metadata.

2. Dynamic routing and metadata
For dynamic routing, I used the generateMetadata function.
Explanations for this can be found in the blog above or in the official Next.js docs.

The key point of this post is that you can use the object used for dynamic routing when creating metadata.
I used the slug and a JSON file that contains the list of posts to create the metadata.
export async function generateMetadata({
params,
}: {
params: { slug: string };
}) {
const { slug } = await params;
// Load the category list JSON
const categoryData = fs.readFileSync(categoryJsonPath, "utf-8");
const categorys: postCardFrontMatter[] = JSON.parse(categoryData);
// Find and return the single entry whose url matches the current slug.
const baseMetaData = categorys.find(
(category) => category.postUrlPath === slug
);
// Use this to set the metadata
const metadata: Metadata = {
...getMetadata({
title: baseMetaData?.data.title,
description: baseMetaData?.data.description,
asPath: `https://earthscience.kr/posts/${slug}`,
ogImage:
baseMetaData?.data.thumbnail &&
`https://earthscience.kr/posts/${slug}/${baseMetaData.data.thumbnail}`,
}),
};
return metadata;
}
3. Thoughts
People usually make good use of Facebook’s Sharing Debugger.
But I don’t have a Facebook account, so I just tested it via KakaoTalk.
The downside is that it takes a bit of time for the image to show up.

At first the image didn’t appear, but when I checked again the next day, it displayed fine.
It feels fun to build things up one by one.
댓글을 불러오는 중...