The Next.js Metadata API gives full-stack developers a typed, component-based way to control SEO, social sharing, and browser behavior directly from your route files. If you're building with the App Router, this is the modern replacement for next/head and manual <meta> tags, and it's worth mastering before you ship your next production app.
The Next.js Metadata API is not just a nice-to-have — it's the standard way to handle search engine optimization in modern Next.js applications. Unlike the old Pages Router approach where you'd manually inject tags, the Metadata API runs on both the server and client, giving you full control over what Google, Twitter, and Facebook see when they crawl your pages. Let me walk you through what actually matters, what doesn't, and how to avoid the mistakes I've seen developers make repeatedly.
Why Next.js Metadata API Matters (and When to Skip It)
Here's my honest take: the Metadata API matters more than most developers think, but it's not a silver bullet. It won't fix poor content or a broken site architecture. What it does is give you a clean, typed interface for the meta tags that search engines and social platforms actually read.
Skip it if you're building a purely internal tool with no public-facing pages, or if you're still on the Pages Router and don't want to migrate. Otherwise, embrace it — it's simpler than manually managing Head components, and it works with React 18's streaming without hydration mismatches.
Getting Started with Next.js Metadata API
The setup is minimal. You export a metadata object (or a generateMetadata function) from your layout.tsx or page.tsx file. Here's the smallest working example:
// app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Suhail Roushan — Full-Stack Developer',
description: 'Portfolio and technical writing on Next.js, TypeScript, and full-stack development.',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
That's it. No imports, no manual <head> manipulation. Next.js handles the rest — it merges this with any page-level metadata you define and renders the correct tags server-side.
For dynamic routes, use generateMetadata:
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';
interface PageProps {
params: { slug: string };
}
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: 'article',
publishedTime: post.date,
},
};
}
Core Next.js Metadata API Concepts Every Developer Should Know
1. Metadata Fields and the title Object
The title field accepts either a string or an object. The object form is powerful because it lets you define default and template values at the layout level, then override just the page-specific part:
// app/layout.tsx
export const metadata: Metadata = {
title: {
default: 'Suhail Roushan',
template: '%s | Suhail Roushan',
},
};
// app/blog/page.tsx
export const metadata: Metadata = {
title: 'Blog', // Renders as "Blog | Suhail Roushan"
};
2. Open Graph and Twitter Cards
Social sharing is where the Metadata API shines. You get full TypeScript validation for openGraph and twitter fields, which prevents typos that silently break your share previews:
// app/products/[id]/page.tsx
export const metadata: Metadata = {
openGraph: {
title: 'Product Name',
description: 'A compelling product description',
images: [{ url: 'https://example.com/og-image.jpg', width: 1200, height: 630 }],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
site: '@suhailroushan',
},
};
3. alternates and Canonical URLs
Duplicate content kills SEO. The alternates field handles canonical URLs and hreflang tags cleanly:
export const metadata: Metadata = {
alternates: {
canonical: '/blog/nextjs-metadata-api',
languages: {
'en-US': '/en-US/blog/nextjs-metadata-api',
'es-ES': '/es-ES/blog/nextjs-metadata-api',
},
},
};
4. robots and verification
Control crawler behavior and verify site ownership without touching robots.txt:
export const metadata: Metadata = {
robots: {
index: true,
follow: true,
googleBot: {
index: true,
'max-image-preview': 'large',
},
},
verification: {
google: 'your-google-verification-code',
},
};
Common Next.js Metadata API Mistakes and How to Fix Them
Mistake 1: Forgetting the metadataBase URL. Without it, relative URLs in openGraph.images or alternates.canonical break in production. Fix it in your root layout:
export const metadata: Metadata = {
metadataBase: new URL('https://suhailroushan.com'),
};
Mistake 2: Putting metadata in a client component. Metadata only works in Server Components. If you're getting "metadata is not supported in client components" errors, move the export to a server component or use generateMetadata with a fetch.
Mistake 3: Ignoring the title.template in nested routes. I've seen sites where the blog post title overwrites the site name entirely. Use the template pattern I showed above — it's the standard way to keep branding consistent across pages.
When Should You Use Next.js Metadata API?
Use it whenever you're building with the App Router and care about search visibility, social sharing, or structured data. The Metadata API is the right choice for public-facing pages, blog posts, product pages, and any route that needs unique SEO tags. If you're building an authenticated dashboard or an internal admin panel, you can safely skip it — those pages don't need to be indexed.
Next.js Metadata API in Production
Tip 1: Test with real crawlers. Don't trust your browser's view source. Use Google's Rich Results Test and Twitter's Card Validator to see exactly what your metadata renders as. I've caught broken image URLs and missing descriptions this way.
Tip 2: Cache generateMetadata calls. If your generateMetadata fetches data from a database, it runs on every request. Use React's cache() or Next.js's built-in fetch caching to avoid hammering your DB:
import { cache } from 'react';
const getPost = cache(async (slug: string) => {
const res = await fetch(`https://api.example.com/posts/${slug}`);
return res.json();
});
Tip 3: Use viewport and themeColor for mobile. These are part of the Metadata API and often overlooked. They're cheap to set and improve the mobile experience:
export const viewport: Viewport = {
themeColor: '#000000',
width: 'device-width',
initialScale: 1,
};
Here's your actionable takeaway: start by adding a metadata export with title, description, and metadataBase to your root layout today — it's a five-minute change that immediately improves how search engines and social platforms represent your site.