Next.js と TypeScript で、vercel/og のOG画像を生成する

TwitterFacebookHatena

TL;DR

Open Graph プロトコルと @vercel/og ライブラリを使用して、ソーシャルメディア画像生成の最適化方法を解説します。動的な Open Graph(OG)画像を生成するために、Vercel の @vercel/og ライブラリを使用して、Vercel Edge Functions を用いてソーシャルカード画像を計算・生成することができます。

つまり、分かりやすくいうと、CSS とテキストを使って画像を自動生成できる ということです。今までのようにアイキャッチ画像や OGP 画像を記事ごとに作る必要が無くなるので、とても便利です。

利点

画像生成に必要なコード量が少ないため、Edge Functions はほぼ瞬時に開始することができます。これにより、画像生成プロセスが迅速に行われ、Open Graph Debugger のようなツールによって認識されます。

HTML と CSS を使用して画像を定義し、ライブラリはマークアップから動的に画像を生成します。

@vercel/og は、エッジで計算済みの画像をキャッシュするための適切なヘッダーを自動的に追加して、コストの削減と再計算の軽減ができるそうです。

サポートされている機能

  • flexbox や絶対位置付けを含む基本的な CSS レイアウト
  • カスタムフォント、テキストの折り返し、中央揃え、ネストされた画像
  • Google Fonts からフォントのサブセット文字をダウンロードする能力
  • Vercel でデプロイされた任意のフレームワークやアプリケーションと互換性

はじめに

最初に @vercel/og をインストールします。

npm i @vercel/og

Open Graph (OG) Image Generation | Vercel Docs を参考に、/pages/api の下に og.tsx を追加して API エンドポイントを作成します。

pages/api/og.tsx

import { ImageResponse } from '@vercel/og'
import { NextRequest } from 'next/server'
import Image from 'next/image'

export const config = {
  runtime: 'edge',
}

export default function handler(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url)

    // ?title=<title>
    const hasTitle = searchParams.has('title')
    const title = hasTitle ? searchParams.get('title')?.slice(0, 100) : 'My default title'

    return new ImageResponse(
      (
        <div
          style={{
            backgroundColor: 'black',
            backgroundSize: '150px 150px',
            height: '100%',
            width: '100%',
            display: 'flex',
            textAlign: 'center',
            alignItems: 'center',
            justifyContent: 'center',
            flexDirection: 'column',
            flexWrap: 'nowrap',
          }}
        >
          <div
            style={{
              fontSize: 60,
              fontStyle: 'normal',
              letterSpacing: '-0.025em',
              color: 'white',
              marginTop: 30,
              padding: '0 120px',
              lineHeight: 1.4,
              whiteSpace: 'pre-wrap',
            }}
          >
            {title}
          </div>
          <div
            style={{
              marginTop: 30,
              display: 'flex',
              // alignItems: 'center',
              justifyContent: 'center',
              justifyItems: 'center',
            }}
          >
            <img alt="" height={75} src="http://localhost:3000/logo.png" style={{ margin: '0 30px' }} width={320} />
          </div>
        </div>
      ),
      {
        width: 1200,
        height: 630,
      }
    )
  } catch (e: any) {
    console.log(`${e.message}`)
    return new Response(`Failed to generate the image`, {
      status: 500,
    })
  }
}

呼び出し

process.env.NODE_ENVを使って現在の環境が開発(ローカル)か本番かを判断することができます。環境によって URL を動的に切り替えるためのコードは以下のようになります:

<img src={`${process.env.BASE_URL || 'http://localhost:3000'}/api/og?title=${postData.id}`} alt="" />

Vercel において環境変数を設定するには、以下の手順を実施します:

  1. Vercel ダッシュボードにログインします。
  2. 対象のプロジェクトを選択します。
  3. プロジェクトダッシュボードの左側のメニューで、"Settings" をクリックします。
  4. "Settings" メニューの中から "Environment Variables" を選択します。
  5. "Add" ボタンをクリックします。
  6. "Name" フィールドに環境変数名(例:BASE_URL)を入力し、"Value" フィールドに対応する値(例:https://あなたのドメイン)を入力します。
  7. "Add" ボタンをクリックして環境変数を保存します。

これで、環境変数は次回のデプロイ時に適用されます。

これにより、ローカル環境では'http://localhost:3000'が使われ、Vercel では設定したBASE_URLが使われます。

ImageResponse コンストラクタ

@vercel/og で、うまく表示できないときは、ImageResponse コンストラクタを使うとよさそうです。

Next.js のImageResponseコンストラクタは、JSX と CSS を使用して動的な画像を生成するためのものです。これは、Open Graph イメージや Twitter カードなどのソーシャルメディアの画像を生成するのに便利です。

以下のようにImageResponseをインポートし、新しいImageResponseオブジェクトを生成することができます:

import { ImageResponse } from 'next/server'

new ImageResponse(
  element: ReactElement,
  options: {
    width?: number = 1200
    height?: number = 630
    emoji?: 'twemoji' | 'blobmoji' | 'noto' | 'openmoji' = 'twemoji',
    fonts?: {
      name: string,
      data: ArrayBuffer,
      weight: number,
      style: 'normal' | 'italic'
    }[]
    debug?: boolean = false

    // Options that will be passed to the HTTP response
    status?: number = 200
    statusText?: string
    headers?: Record<string, string>
  },
)

Functions: ImageResponse | Next.js

この構造体には、以下のオプションが用意されています。

オプション データ型 デフォルト値 説明
element ReactElement - 描画する ReactElement
width number 1200 画像の幅
height number 630 画像の高さ
emoji string ('twemoji' | 'blobmoji' | 'noto' | 'openmoji') 'twemoji' 使用する絵文字のセット
fonts Array - 画像内で使用するカスタムフォントの配列。各フォントは、フォント名、フォントデータ(ArrayBuffer)、フォントの重み、およびスタイル('normal'または'italic')を指定します
debug boolean false デバッグモードを有効にするかどうか
status number 200 HTTP レスポンスのステータスコード
statusText string - HTTP レスポンスのステータステキスト
headers Record<string, string> - HTTP レスポンスに追加するヘッダーのレコード

Next.js と TypeScript で、vercel/og のOG画像を生成する