Skip to main content
Practice Problems

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

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems