🎞️

Next.jsでOGPを設定する方法

に公開

Next.js で OGP を設定する方法

Next.js で OGP(Open Graph Protocol)を設定することで、SNS でシェアされた際に適切なプレビュー画像とメタ情報を表示できます。本記事では、Next.js の App Router を使用した OGP 設定の実装方法を紹介します。

はじめに

OGP を設定することで、以下のようなメリットがあります:

  • SNS でシェアされた際に、魅力的なプレビュー画像と情報が表示される
  • SEO の向上
  • クリック率の向上

Next.js では、opengraph-image.tsxというファイルコンベンションを使用することで、動的に OGP 画像を生成できます。

1. OGP 画像の生成

opengraph-image.tsx の作成

Next.js では、opengraph-image.tsxというファイルを作成することで、そのルート用の OGP 画像を自動生成できます。

まず、トップページ用の OGP 画像を作成します:

src/app/opengraph-image.tsx
import { generateOGPImage } from '@/utils/ogp/generate-image'

export default async function Image() {
  return generateOGPImage({
    title: 'サイト名',
    subtitle: 'サイトの説明',
  })
}

動的なルート(例:/posts/[id])の場合も、同様にopengraph-image.tsxを作成できます:

src/app/posts/[id]/opengraph-image.tsx
import { generateOGPImage } from '@/utils/ogp/generate-image'
import { getPost } from '@/lib/posts'

export default async function Image({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const post = await getPost(id)

  if (!post) {
    // エラー時のフォールバック画像を返す
    return generateOGPImage({
      title: '記事が見つかりません',
    })
  }

  return generateOGPImage({
    title: post.title,
    subtitle: post.description,
  })
}

OGP 画像生成ユーティリティ

OGP 画像を生成するためのユーティリティ関数を作成します。Next.js の ImageResponse を使用して、React コンポーネントから画像を生成できます。

このユーティリティでは、以下の要素で構成されています:

  • OGP_SIZE: OGP 画像の推奨サイズ(1200×630px)を定義
  • CardContent: 画像内に表示するコンテンツ(タイトルとサブタイトル)を表示するコンポーネント
  • Container: 背景グラデーションとレイアウトを管理するコンポーネント
  • generateOGPImage: これらのコンポーネントを組み合わせて、ImageResponse で画像を生成する関数
src/utils/ogp/generate-image.tsx
import { ImageResponse } from 'next/og'

export const OGP_SIZE = {
  width: 1200,
  height: 630,
}

interface CardContent {
  title: string
  subtitle?: string
}

function CardContent({ title, subtitle }: CardContent) {
  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        padding: '80px',
        backgroundColor: 'white',
        borderRadius: '24px',
        boxShadow:
          '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
        maxWidth: '1000px',
      }}
    >
      <h1
        style={{
          fontSize: '64px',
          fontWeight: 'bold',
          color: '#1f2937',
          marginBottom: subtitle ? '40px' : '20px',
          textAlign: 'center',
          maxWidth: '900px',
        }}
      >
        {title}
      </h1>
      {subtitle && (
        <p
          style={{
            fontSize: '28px',
            color: '#6b7280',
            textAlign: 'center',
          }}
        >
          {subtitle}
        </p>
      )}
    </div>
  )
}

function Container({ children }: { children: React.ReactNode }) {
  return (
    <div
      style={{
        height: '100%',
        width: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        background: 'linear-gradient(to bottom, #3b82f6, #2563eb)',
      }}
    >
      {children}
    </div>
  )
}

export function generateOGPImage(content: CardContent) {
  return new ImageResponse(
    (
      <Container>
        <CardContent {...content} />
      </Container>
    ),
    {
      width: OGP_SIZE.width,
      height: OGP_SIZE.height,
    },
  )
}

2. メタデータの設定

layout.tsx でのメタデータ設定

layout.tsxで OGP 画像を参照するように設定します:

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

export const metadata: Metadata = {
  title: 'サイト名 - サイトの説明',
  description: 'サイトの説明文',
  openGraph: {
    title: 'サイト名 - サイトの説明',
    description: 'サイトの説明文',
    url: 'https://example.com',
    siteName: 'サイト名',
    type: 'website',
    locale: 'ja_JP',
    images: [
      {
        url: '/opengraph-image',
        width: 1200,
        height: 630,
        alt: 'サイト名 - サイトの説明',
      },
    ],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'サイト名 - サイトの説明',
    description: 'サイトの説明文',
    images: ['/opengraph-image'],
  },
}

動的ルートでのメタデータ生成

動的なルートでは、generateMetadata関数を使用してメタデータを生成します:

src/app/posts/[id]/page.tsx
import type { Metadata } from 'next'
import { getPost } from '@/lib/posts'

export async function generateMetadata({
  params,
}: {
  params: Promise<{ id: string }>
}): Promise<Metadata> {
  const { id } = await params
  const post = await getPost(id)

  if (!post) {
    return {
      title: '記事が見つかりません',
      description: '指定された記事は存在しません。',
    }
  }

  return {
    title: `${post.title} | サイト名`,
    description: post.description,
    openGraph: {
      title: post.title,
      description: post.description,
      type: 'article',
      url: `https://example.com/posts/${id}`,
      siteName: 'サイト名',
      images: [`https://example.com/posts/${id}/opengraph-image`],
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.description,
      images: [`https://example.com/posts/${id}/opengraph-image`],
    },
  }
}

3. 共通メタデータの管理

コードの重複を避けるため、共通のメタデータを管理する関数を作成します:

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

export function createBaseMetadata(): Metadata {
  return {
    metadataBase: new URL('https://example.com'),
    robots: {
      index: true,
      follow: true,
      googleBot: {
        index: true,
        follow: true,
        'max-video-preview': -1,
        'max-image-preview': 'large',
        'max-snippet': -1,
      },
    },
    openGraph: {
      siteName: 'サイト名',
      type: 'website',
      locale: 'ja_JP',
    },
    twitter: {
      card: 'summary_large_image',
    },
  }
}

各ページのメタデータ生成関数で、この共通メタデータを使用します:

src/app/posts/[id]/page.tsx
import { createBaseMetadata } from '@/lib/metadata'
import type { Metadata } from 'next'
import { getPost } from '@/lib/posts'

export async function generateMetadata({
  params,
}: {
  params: Promise<{ id: string }>
}): Promise<Metadata> {
  const { id } = await params
  const baseMetadata = createBaseMetadata()
  const post = await getPost(id)

  if (!post) {
    return {
      ...baseMetadata,
      title: '記事が見つかりません',
      description: '指定された記事は存在しません。',
    }
  }

  return {
    ...baseMetadata,
    title: `${post.title} | サイト名`,
    description: post.description,
    openGraph: {
      ...baseMetadata.openGraph,
      title: post.title,
      description: post.description,
      url: `https://example.com/posts/${id}`,
      images: [`https://example.com/posts/${id}/opengraph-image`],
    },
    twitter: {
      ...baseMetadata.twitter,
      title: post.title,
      description: post.description,
      images: [`https://example.com/posts/${id}/opengraph-image`],
    },
  }
}

まとめ

Next.js で OGP を設定する方法を紹介しました。主なポイントは以下の通りです:

  1. opengraph-image.tsxを使用した動的 OGP 画像生成: Next.js のファイルコンベンションを活用することで、簡単に OGP 画像を生成できます
  2. メタデータの適切な設定: openGraphtwitterのメタデータを設定することで、SNS での表示を最適化できます
  3. 共通メタデータの管理: コードの重複を避け、保守性を向上させるため、共通メタデータを管理する関数を作成します

これらの実装により、SNS でシェアされた際に適切なプレビュー画像と情報が表示され、SEO の向上にもつながります。

参考リンク

Discussion