t28.dev

OGP 画像を作る時に @vercel/og を使うか satori を使うか迷ったログ

2023/6/3
Tech

動的な OGP 画像の出力手段として、@vercel/ogsatori をよく聞くようになりましたね。 「自分のブログ (ここ) もこれらを使って OGP 画像を作りたいけど…どっち使おう?」ってなったので、色々調べたことをメモします。

satori?

To use Satori in your project to generate PNG images like Open Graph images and social cards, check out our announcement and Vercel’s Open Graph Image Generation

https://github.com/vercel/satori

って書いているし、@vercel/og 使えば良さそうだな… (終わり)

@vercel/og?

私のお気持ち

🤔 satori を使って自分で Options やら画像変換やらをしてもいいんだけれど… @vercel/og に乗っかるだけで済む方が嬉しいな…

@vercel/og がやっていること

@vercel/og はリポジトリを公開していない1ので、unpkg でソースコードを覗いてみる。

https://www.unpkg.com/@vercel/[email protected]/dist/index.node.js

@vercel/og で実装しているコードは少なかった

  • src/index.node.ts
  • src/emoji/index.ts
  • src/og.ts

src/index.node.ts

型情報 からはどんなクラスがさっぱり分からない ImageResponse class が定義されている。

ImageResponse (の constructor) でやっていること

  1. satori, @resvg+resvg-wasm の描画結果 (PNG 画像) から ReadableStream を作る
  2. ReadableStream から Response オブジェクトの作成を作る
    • "content-type": "image/png" の設定 (README に書いてあるやつ)
    • "cache-control": "public, immutable, no-transform, max-age=31536000" の設定 (README に書いてあるやつ)
      • NODE_ENV === "development" のときは "no-cache, no-store"
    • constructor 内で return new Response() をしているので、ImageResponse class のインスタンスは実質 Response オブジェクト

src/emoji/index.ts

options.emoji の型に対応した、各 emoji リソースや読み込み用関数の定義をしている

src/og.ts

render() が主な関数

  • default option の設定 (README にかいてあるやつ)
    • width: 1200
    • height: 630
    • debug: false
  • satori() 実行
    • options.fonts を指定されていない場合、@vercel/og の default (noto-sans-v27-latin-regular.ttf) を使う
    • options.loadAdditionalAssetoptions.emoji で指定した emoji を読み込む関数を渡している
  • @resvg+resvg-wasm で SVG を PNG に変換する

satori が受け付ける options (README に書いていないからコードから…) と比較すると

  • @vercel/og でも同じ用に渡せる
    • width: number
    • height: number
    • fonts: FontOptions[]
    • debug?: boolean
  • @vercel/og で wrap? してくれている
    • loadAdditionalAsset?: (languageCode: string, segment: string ) => Promise<string | Array<FontOptions>>
      • 'twemoji' | 'blobmoji' | 'noto' | 'openmoji' | 'fluent' | 'fluentFlat' の中から選んだ emoji を読み込んでくれる
  • @vercel/og で渡せない
    • embedFont?: boolean
    • graphemeImages?: Record<string, string>
    • tailwindConfig?: TwConfig
    • onNodeDetected?: (node: SatoriNode) => void

つまり

@vercel/og の中では

  • satori 用の default 値 (width, height, fonts) を渡したり、
  • satori とは別で必要な処理 (loadAdditionalAsset, google font 読み込み, emoji 読み込み) をやってくれたり、
  • satori で React element から SVG を作ってくれたり、
  • satori の出力結果 (SVG) を PNG にしてくれたり、
  • HTTP Header 付きの Response オブジェクトを作ったり

してくれている。

私の結論

✌️😁 @vercel/og で OGP 画像を作る!

=> “@vercel/og を使って、Astro 製ブログ のビルド時にページごとの OGP 画像を出力する

Footnotes

  1. 記事を書いた時点で https://www.npmjs.com/package/@vercel/og を見る限りは、リポジトリを公開していない。satori と LICENSE は同じ(Mozilla Public License Version 2.0)だし、minify, bundle された js は npm で公開している以上見られるけれど…。