Metadata trong App Router là API khai báo để Next.js tự sinh các thẻ trong <head> như title, description, openGraph, robots. Bạn định nghĩa nó bằng cách export const metadata cho giá trị tĩnh hoặc export async function generateMetadata() cho giá trị động, đặt trong layout.tsx hoặc page.tsx.
Next.js thực thi metadata và generateMetadata chỉ trên server, lúc build với route tĩnh hoặc mỗi request với route động. Với mỗi URL, Next.js thu thập metadata từ layout.tsx gốc qua các layout lồng nhau đến page.tsx rồi merge từ ngoài vào trong, giá trị ở segment con ghi đè cha. Nhờ vậy bạn có thể đặt title.template chung ở root layout và chỉ override phần riêng ở từng page. generateMetadata nhận { params, searchParams } và tham số thứ hai parent chứa metadata đã merge của cha, cho phép kế thừa và mở rộng. Kết quả được render thẳng vào HTML trên server nên thay thế next/head của Pages Router và đảm bảo crawler nhận đủ thẻ SEO ngay từ phản hồi đầu.
// app/blog/layout.tsx — title.template phải đặt ở layout cha, đặt trong page.tsx không có tác dụng
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: { default: 'Blog', template: '%s | Acme' },
description: 'Bài viết về Next.js',
};
// app/blog/[slug]/page.tsx — một segment chỉ được export metadata HOẶC generateMetadata
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params; // Next 15+: params là Promise
const post = await fetchPost(slug); // fetch dữ liệu trên server
return {
title: post.title,
description: post.excerpt,
openGraph: { images: [post.coverImage] },
};
}- Đặt
export const metadatatrong file có'use client'sẽ bị bỏ qua vì metadata chỉ hoạt động trong Server Component. - Từ Next.js 15,
paramsvàsearchParamstronggenerateMetadatalàPromisenên cầnawait paramstrước khi đọc thuộc tính. - Không được export đồng thời
metadatavàgenerateMetadatatrong cùng một route segment — Next.js báo lỗi build.
Lưu ý: Dùng metadata tĩnh khi tiêu đề và mô tả cố định, dùng generateMetadata khi cần fetch dữ liệu theo params hoặc cá nhân hoá theo request; đừng chèn thẻ <title> thủ công trong JSX khi đã dùng API này.