nextjs

Next.js Metadata Helper

Reusable helper function for generating SEO-optimized metadata in Next.js App Router.

#seo #metadata #app-router

A type-safe helper to generate consistent metadata across your Next.js app.

createMetadata Helper

// lib/metadata.ts
import type { Metadata } from 'next';

interface MetadataParams {
  title: string;
  description: string;
  path?: string;
  image?: string;
  type?: 'website' | 'article';
  publishedTime?: string;
  noIndex?: boolean;
}

const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'https://example.com';
const SITE_NAME = 'Your Site Name';
const DEFAULT_IMAGE = '/og-image.jpg';

export function createMetadata({
  title,
  description,
  path = '',
  image = DEFAULT_IMAGE,
  type = 'website',
  publishedTime,
  noIndex = false,
}: MetadataParams): Metadata {
  const url = `${BASE_URL}${path}`;
  const imageUrl = image.startsWith('http') ? image : `${BASE_URL}${image}`;

  return {
    title,
    description,
    metadataBase: new URL(BASE_URL),
    alternates: { canonical: url },
    robots: noIndex ? { index: false, follow: false } : undefined,
    openGraph: {
      title,
      description,
      url,
      siteName: SITE_NAME,
      type,
      images: [{ url: imageUrl, width: 1200, height: 630, alt: title }],
      ...(publishedTime && { publishedTime }),
    },
    twitter: {
      card: 'summary_large_image',
      title,
      description,
      images: [imageUrl],
    },
  };
}

Usage in Pages

// app/page.tsx
import { createMetadata } from '@/lib/metadata';

export const metadata = createMetadata({
  title: 'Home - My Site',
  description: 'Welcome to my website',
  path: '/',
});

Usage for Blog Posts

// app/blog/[slug]/page.tsx
import { createMetadata } from '@/lib/metadata';
import { getPost } from '@/lib/posts';

export async function generateMetadata({ params }: Props) {
  const post = await getPost(params.slug);
  
  return createMetadata({
    title: post.title,
    description: post.excerpt,
    path: `/blog/${params.slug}`,
    image: post.coverImage,
    type: 'article',
    publishedTime: post.publishedAt,
  });
}

Dynamic Metadata Template

// For title templates
export const metadata: Metadata = {
  title: {
    template: '%s | My Site',
    default: 'My Site',
  },
};

Key features:

  • Consistent OpenGraph and Twitter cards
  • Automatic canonical URLs
  • Article metadata support
  • noIndex option for private pages