SEO Magic

Next.js SEO Metadata

Set up the perfect SEO metadata for your Next.js site in App Router

My SEO strategy for Google

This is my Next.js metadata to create on-page SEO metadata in App Router.

My SEO script

// /lib/seo.tsx
import { type Metadata } from 'next'
import { type OpenGraph } from 'next/dist/lib/metadata/types/opengraph-types'
import { type Twitter } from 'next/dist/lib/metadata/types/twitter-types'
import { type StaticImageData } from 'next/image'
 
const title = 'Your Title'
const description = `Your SEO description`
 
export const rootOpenGraph: OpenGraph = {
  type: 'website',
  url: 'https://yourdomain.com',
  siteName: 'Your Site',
  title,
  description,
}
 
export const rootTwitter: Twitter = {
  title,
  description,
  card: 'summary_large_image',
  creator: '@yourtwitter',
}
 
export const rootMetadata: Metadata = {
  metadataBase: new URL('https://yoursite.com'),
  title,
  description,
  applicationName: 'Your App',
  openGraph: rootOpenGraph,
  twitter: rootTwitter,
  themeColor: '#362168',
  icons: [
    {
      rel: 'apple-touch-icon',
      sizes: '180x180',
      url: '/_static/favicons/apple-touch-icon.png',
    },
    {
      rel: 'icon',
      type: 'image/png',
      sizes: '32x32',
      url: '/_static/favicons/favicon-32x32.png',
    },
    {
      rel: 'icon',
      type: 'image/png',
      sizes: '16x16',
      url: '/_static/favicons/favicon-16x16.png',
    },
    {
      rel: 'manifest',
      url: '/site.webmanifest',
    },
    {
      rel: 'mask-icon',
      url: '/_static/favicons/safari-pinned-tab.svg',
      color: '#5bbad5',
    },
  ],
 
  robots: 'index, follow, max-image-preview:large',
}
 
function getImage(image?: StaticImageData | string) {
  if (!image) {
    return null
  }
 
  if (typeof image === 'string') {
    return {
      url: image,
    }
  }
 
  return {
    url: image.src,
    width: image.width,
    height: image.height,
    alt: title,
  }
}
 
export function generatePageMeta({
  title = 'Your Title',
  description = `Your SEO description`,
  url,
  image,
  date,
  author,
  siteName,
  feed,
}: {
  title?: string
  description?: string
  url?: string
  image?: StaticImageData | string
  date?: string
  author?: string
  siteName?: string
  feed?: string
} = {}): Metadata {
  const metadata = {
    ...rootMetadata,
    title,
    description,
    alternates: {
      canonical: url,
    },
    openGraph: {
      ...rootOpenGraph,
      url,
      title,
      description,
    } as OpenGraph,
    twitter: {
      ...rootTwitter,
      title,
      description,
    } as Twitter,
  } as Metadata
 
  if (date && author) {
    metadata.openGraph = {
      ...metadata.openGraph,
      type: 'article',
      publishedTime: date,
      authors: [author],
    }
  }
 
  const img = getImage(image)
  if (img) {
    metadata.openGraph!.images = [img]
    metadata.twitter!.images = [img]
  }
 
  if (siteName) {
    metadata.applicationName = siteName
    metadata.openGraph!.siteName = siteName
  }
 
  if (feed) {
    if (!metadata.alternates!.types) {
      metadata.alternates!.types = {}
    }
    metadata.alternates!.types['application/rss+xml'] = feed
  } else {
    metadata.alternates!.types = {
      'application/rss+xml': `${process.env.NEXT_PUBLIC_SITE_URL}/feed.xml`,
    }
  }
 
  return metadata
}

Layout.tsx

import { generatePageMeta } from '@/lib/seo'
export const metadata = generatePageMeta()

Page.tsx

import { generatePageMeta } from '@/lib/seo'
export const metadata = generatePageMeta({
  title: `Hello World`,
  description: `My site is about...`,
  url: `/`,
})
Grow your business

We're a full-service SEO agency and we grow businesses by 200% on average in 6 months.

Work With Us