Metadata and SEO in Next.js
Next.js has a built-in metadata management system for SEO. Metadata can be set statically via the metadata object or dynamically via the generateMetadata function.
Static Metadata
tsx
// app/layout.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'IT Lead',
description: 'Frontend interview preparation platform',
keywords: ['frontend', 'interview', 'javascript', 'react']
}Dynamic Metadata
For pages where metadata depends on data, use generateMetadata:
tsx
// app/docs/[slug]/page.tsx
import type { Metadata } from 'next'
import { getDoc } from '@/lib/docs'
export async function generateMetadata({
params
}: {
params: { slug: string }
}): Promise<Metadata> {
const doc = await getDoc(params.slug)
return {
title: doc.title,
description: doc.description,
keywords: doc.keywords,
openGraph: {
title: doc.title,
description: doc.description,
type: 'article'
}
}
}Next.js deduplicates requests: if generateMetadata and the page component call the same fetch, the request runs once.
Open Graph and Twitter Cards
tsx
export const metadata: Metadata = {
title: 'JavaScript Interview Problems',
openGraph: {
title: 'JavaScript Interview Problems',
description: 'Solve problems from real interviews',
url: 'https://itlead.org/problems',
siteName: 'IT Lead',
images: [
{
url: '/images/ITLeadBanner.png',
width: 1200,
height: 630
}
],
locale: 'en_US',
type: 'website'
},
twitter: {
card: 'summary_large_image',
title: 'JavaScript Interview Problems',
description: 'Solve problems from real interviews',
images: ['/images/ITLeadBanner.png']
}
}Title Templates
To add a suffix or prefix to child page titles:
tsx
// app/layout.tsx
export const metadata: Metadata = {
title: {
template: '%s | IT Lead',
default: 'IT Lead'
}
}
// app/docs/page.tsx
export const metadata: Metadata = {
title: 'Knowledge Base'
}
// Result: "Knowledge Base | IT Lead"Sitemap
Next.js can generate a sitemap automatically:
tsx
// app/sitemap.ts
import { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
const docs = getAllDocs()
return [
{ url: 'https://itlead.org', lastModified: new Date() },
{ url: 'https://itlead.org/problems', lastModified: new Date() },
...docs.map(doc => ({
url: `https://itlead.org/docs/${doc.slug}`,
lastModified: doc.updatedAt
}))
]
}robots.txt
tsx
// app/robots.ts
import { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: ['/api/', '/auth/']
},
sitemap: 'https://itlead.org/sitemap.xml'
}
}Structured Data (JSON-LD)
For rich snippets in search results:
tsx
// app/docs/[slug]/page.tsx
export default async function DocPage({ params }) {
const doc = await getDoc(params.slug)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: doc.title,
description: doc.description,
author: { '@type': 'Organization', name: 'IT Lead' }
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<article>{doc.content}</article>
</>
)
}Interview tip:
Metadata is inherited from parent segments to children and merged. A child segment can override any field from the parent.
Useful Resources
- nextjs.org/docs — Metadata
- generateMetadata API
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.