Have you heard about Hack Frontend Community?Join us on Telegram!

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

// app/layout.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Hack Frontend',
  description: 'Frontend interview preparation platform',
  keywords: ['frontend', 'interview', 'javascript', 'react']
}

Dynamic Metadata

For pages where metadata depends on data, use generateMetadata:

// 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

export const metadata: Metadata = {
  title: 'JavaScript Interview Problems',
  openGraph: {
    title: 'JavaScript Interview Problems',
    description: 'Solve problems from real interviews',
    url: 'https://hackfrontend.com/problems',
    siteName: 'Hack Frontend',
    images: [
      {
        url: '/images/HackFrontendBanner.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/HackFrontendBanner.png']
  }
}

Title Templates

To add a suffix or prefix to child page titles:

// app/layout.tsx
export const metadata: Metadata = {
  title: {
    template: '%s | Hack Frontend',
    default: 'Hack Frontend'
  }
}

// app/docs/page.tsx
export const metadata: Metadata = {
  title: 'Knowledge Base'
}
// Result: "Knowledge Base | Hack Frontend"

Sitemap

Next.js can generate a sitemap automatically:

// app/sitemap.ts
import { MetadataRoute } from 'next'

export default function sitemap(): MetadataRoute.Sitemap {
  const docs = getAllDocs()

  return [
    { url: 'https://hackfrontend.com', lastModified: new Date() },
    { url: 'https://hackfrontend.com/problems', lastModified: new Date() },
    ...docs.map(doc => ({
      url: `https://hackfrontend.com/docs/${doc.slug}`,
      lastModified: doc.updatedAt
    }))
  ]
}

robots.txt

// app/robots.ts
import { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: ['/api/', '/auth/']
    },
    sitemap: 'https://hackfrontend.com/sitemap.xml'
  }
}

Structured Data (JSON-LD)

For rich snippets in search results:

// 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: 'Hack Frontend' }
  }

  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

By continuing to use the platform, you accept the terms of the Privacy Policy and the use of cookies.