Skip to content

Next.js - Meta Data

Modifying the <head> section of your HTML pages is essential for SEO (Search Engine Optimization), social media sharing (Open Graph tags), and providing context to the browser (like setting the page title).

Next.js provides different ways to manage metadata depending on whether you are using the App Router or the Pages Router.

The App Router introduces a powerful Metadata API. You can define metadata by exporting a static metadata object or a dynamic generateMetadata function from a layout.js or page.js file.

Static Example (app/about/page.js):

import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'About Us | My Awesome Site',
description: 'Learn more about our company and team.',
openGraph: {
title: 'About Us | My Awesome Site',
description: 'Learn more about our company and team.',
// images: ['/path/to/image.jpg'],
},
};
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>This page uses the static Metadata API.</p>
</div>
);
}

Dynamic Example (app/posts/[slug]/page.js): Fetches data to generate metadata.

import { Metadata, ResolvingMetadata } from 'next';
// Function to fetch post data (replace with your actual data fetching logic)
async function getPostData(slug) {
// const res = await fetch(`.../api/posts/${slug}`)
// const post = await res.json()
// return post
return { title: `Post: ${slug}`, excerpt: `Details about post ${slug}` };
}
type Props = {
params: { slug: string };
searchParams: { [key: string]: string | string[] | undefined };
};
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const slug = params.slug;
// fetch data
const post = await getPostData(slug);
// optionally access and extend (rather than replace) parent metadata
// const previousImages = (await parent).openGraph?.images || [];
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
// images: ['/some-specific-post-image.jpg', ...previousImages],
},
};
}
export default async function PostPage({ params }: Props) {
const post = await getPostData(params.slug);
return (
<div>
<h1>{post.title}</h1>
<p>This page uses the dynamic generateMetadata function.</p>
</div>
);
}

The Metadata API automatically handles merging metadata from parent layouts and deduplicating tags, making it robust and easier to manage.

In the Pages Router, you use the built-in Head component imported from next/head. Any tags you place inside <Head> will be appended to the <head> section of the final HTML.

Let’s update sample pages (pages/index.js and pages/posts/first-post.js) to include titles and meta descriptions using <Head>.

Update pages/index.js:

import Link from 'next/link';
import Head from 'next/head'; // Import Head
function HomePage() {
return (
<>
{/* Add Head component */}
<Head>
<title>Welcome to My Next.js Site!</title>
<meta name="description" content="The homepage of my awesome Next.js application" />
{/* You can add other tags like Open Graph meta tags here */}
<meta property="og:title" content="Welcome to My Next.js Site!" />
</Head>
<div>
<h1>Welcome to Next.js!</h1>
<Link href="/posts/first-post">
<a>Go to First Post</a>
</Link>
</div>
</>
);
}
export default HomePage;

Update pages/posts/first-post.js:

import Link from 'next/link';
import Head from 'next/head'; // Import Head
import Layout from '../../components/layout'; // Assuming Layout component exists
export default function FirstPost() {
return (
<Layout>
{/* Add Head component */}
<Head>
<title>My First Post | Next.js Blog</title>
<meta name="description" content="Reading the very first post on this blog." />
</Head>
<h1>My First Post</h1>
<p>This is the content of the first post.</p>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</Layout>
);
}

If multiple components in the render tree use <Head> with the same tag (e.g., multiple <title> tags), Next.js will automatically deduplicate them, typically keeping the last one defined. To ensure uniqueness for tags like <meta name="description">, add a unique key prop: <meta name="description" key="desc" content="..." />.

Start the server if it’s not running:

npm run dev
# Example Output:
# ready - started server on 0.0.0.0:3000, url: http://localhost:3000
  1. Open http://localhost:3000 in your browser. Check the browser tab title - it should display ‘Welcome to My Next.js Site!’. View the page source (Right-click -> View Page Source or use browser developer tools) and inspect the <head> section. You should find the <title> and <meta name="description"> tags as defined in pages/index.js.

  2. Click the link to navigate to the ‘First Post’ (/posts/first-post). Observe that the browser tab title changes to ‘My First Post | Next.js Blog’. Inspect the page source again; the <head> section should now contain the metadata defined in pages/posts/first-post.js.

Properly managing metadata is crucial for how search engines index your site and how links appear when shared on social media platforms.