Megu
今更ながらVercel OG Image Generationを個人ブログに導入する

今更ながらVercel OG Image Generationを個人ブログに導入する

2023.02.05

Next.js
Vercel

これまでは、メイン画像をFigmaで作成してmicroCSM側に記事と一緒に登録しており記事のメイン画像&OGP画像として使用するような仕組みにしていました。
問題点としては毎回メイン画像を作成する手間とメインとOGP画像に別々の画像を指定できないことです。
色々なデザインの画像を作成することが出来るのは良いのですが、その分簡単な記事などを書くときは少し手間に感じていました...
そこでメイン画像とOGP画像を切り分けることにしました。
(メイン画像に好きな画像を差し込めるようになるのでもっと早く導入すべきでした...w)

OG Image Generation

OG Image GenerationVercel Edge Functions を使用したOG画像の生成に関しての題材があります。
Edge Functionsは簡単にいうとVercelがもつCDNのエッジにて簡単な処理を実行することができる機能です。
https://og-playground.vercel.app/ で簡単に確認できるので一度見てみて下さい。

OG Imageの生成には「satori」とResvgでHTML, CSSをPNGに変換している。

参考のサンプルも記載があるので下記から確認してみて下さい。
https://vercel.com/docs/concepts/functions/edge-functions/og-image-examples

今回作成したOGP画像

pages/api/ogの調整

このブログにはChakraUIを使用しているので導入したかったのですが少し手間がかかりそうだったので断念。
configの設定で runtime: 'experimental-edge' を有効にするのをお忘れなく。

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

export const config = {
  runtime: 'experimental-edge'
}

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

    const hasTitle = searchParams.has('title')
    const title = hasTitle
      ? searchParams.get('title')?.slice(0, 100)
      : 'megumu.me'

    return new ImageResponse(
      (
        <div
          style={{
            display: 'flex',
            justifyContent: 'center',
            height: '100%',
            width: '100%',
            alignItems: 'center',
            background: 'linear-gradient(135deg, #6139FD, #974FFE, #C260FE)'
          }}
        >
          <div
            style={{
              backgroundColor: 'white',
              border: 'none',
              height: '84%',
              width: '88%',
              display: 'flex',
              textAlign: 'left',
              alignItems: 'center',
              justifyContent: 'center',
              flexDirection: 'column',
              flexWrap: 'nowrap',
              padding: 40,
              position: 'relative',
              borderRadius: 50
            }}
          >
            <div
              style={{
                fontSize: 54,
                color: 'black',
                lineHeight: 1.3,
                width: '100%'
              }}
            >
              {title}
            </div>
            <div
              style={{
                display: 'flex',
                gap: 8,
                alignItems: 'center',
                position: 'absolute',
                bottom: 20,
                right: 40
              }}
            >
              <div
                style={{
                  width: '40px',
                  height: '40px',
                  borderRadius: '50%',
                  background:
                    'linear-gradient(135deg, #6139FD, #974FFE, #C260FE)'
                }}
              ></div>
              <p style={{ fontSize: 34, marginTop: 10 }}>megumu.me</p>
            </div>
          </div>
        </div>
      ),
      {
        width: 1280,
        height: 640
      }
    )
  } catch (e: any) {
    console.log(`${e.message}`)
    return new Response(`Failed to generate the image`, {
      status: 500
    })
  }
}