ブログ記事のサムネイル・OGP 画像 — Satori でビルド時に自動生成する

  • Astro
  • OGP
  • Satori
  • ブログ運用

よしあきの Astro 技術ブログでは、記事ごとに サムネイル用 PNG を手で置かない。代わりに ビルド時に Satori で画像を組み立て、次の3か所で 同じ URL を参照する。

用途参照元
記事一覧のカードBlogPostList.astro
記事ページ上部のヒーローBlogPost.astro
og:image / X のカード画像BaseHead.astro

画像に載る文字は title(記事タイトル)とサイト名 だけ。description はメタタグ用で、画像には描かない。

読み方

  1. 出力の流れ
  2. URL とファイルの対応
  3. デザインと寸法
  4. 記事を書いたあとにやること
  5. テンプレを変えたとき
  6. heroImage について(レガシー)
  7. まとめ

出力の流れ

ビルド(npm run build)または開発サーバー起動時に、Astro の プリレンダー API ルート が PNG を生成する。

frontmatter の title

src/pages/og/[...slug].png.ts  … 公開記事 slug ごとに getStaticPaths

renderOgPng()(src/og/renderOgPng.ts)
    ├─ createOgTemplate() … Satori 用の要素ツリー(src/og/template.ts)
    ├─ satori() … SVG に変換(1200×630)
    └─ sharp() … PNG バイナリへ

配信パス /og/{記事のベース名}.png

サイト全体用のデフォルト画像は /og.pngsrc/pages/og.png.ts)。記事ページでは slug 付きの /og/2026-06-12.png のように ファイル名(拡張子除く)= Content Collection の id が揃う。

下書きdraft: true)は本番ビルドでは isBlogEntryPublished により OG ルートの対象外 になる。開発サーバー(astro dev)では下書きも含めてプレビューできる。

フォントは @fontsource/noto-sans-jp の WOFF を src/og/fonts.ts が読み込む。日本語タイトルが崩れないように、ビルドパイプライン内で完結させている。


URL とファイルの対応

表示側は getOgImagePath(slug)src/utils/ogImageUrl.ts)でパスを組み立てる。

// slug あり: /og/2026-06-12.png?v=3
// slug なし: /og.png?v=3

?v= の数字は src/consts.tsOG_IMAGE_CACHE_VERSION。テンプレの見た目を変えたあと、SNS やブラウザが古い PNG を掴み続けないための キャッシュ bust 用。

BaseHead.astro では getOgImageAbsoluteUrl絶対 URL にし、og:imagetwitter:imageog:image:width / height(1200 / 630)を出力する。


デザインと寸法

生成サイズ1200×630(比率 40:21)。X や Open Graph のリンクプレビューでよく使われる比率に合わせている。

画面上の <img> は同じ比率で縮小表示する。目安は consts.ts の次の定数。

定数表示幅 × 高さ使う場所
BLOG_HERO_DISPLAY1020 × 536記事ヒーロー
BLOG_LIST_THUMB_DISPLAY720 × 378一覧カード

配色・レイアウトの正本は src/og/template.tscreateOgTemplate。クリーム系の背景、左のティールのアクセント帯、白いカード内にタイトル、下にサイト名、という フラットな1枚絵 だ。以前、画像生成 AI で1枚ずつ作っていたときのトーンを、コードに固定した。

タイトルが長いときは 80 文字で省略(末尾 )。一覧と OG で同じ見た目になるので、タイトルは短めに書くか、省略を許容する前提でよい。


記事を書いたあとにやること

新規記事 src/content/blog/2026-06-12.md を追加したら、サムネ用の画像ファイルは不要。やることは次のとおり。

  1. frontmatter に titledescriptionpubDate を書く(スキーマ必須)
  2. npm run build またはデプロイでビルドする
  3. 本番では draft: false(または draft 省略)にして公開

プレビューは開発サーバーで記事 URL を開く。ヒーローと /og/{slug}.png が同じ絵になる。

以前の運用では src/assets/blog/hero/ に PNG を置き、heroImage で frontmatter から参照していた。いまは 表示・OG とも Satori 生成に一本化 している(README も同方針)。


テンプレを変えたとき

  1. src/og/template.ts を編集(色・余白・フォントサイズなど)
  2. OG_IMAGE_CACHE_VERSION を increment(src/consts.ts
  3. 再ビルド・再デプロイ

renderOgPng.ts を触る必要は、SVG→PNG の変換方式を変えない限りほぼない。フォントを増やすときは fonts.tspackage.json@fontsource/* を揃える。

依存パッケージは satorisharp@fontsource/noto-sans-jppackage.json 参照)。


heroImage について(レガシー)

content.config.ts では heroImage をまだ任意フィールドとして受け付ける。ただし レイアウトのヒーロー・一覧・og:image には使わない

本文 MDX で <Image src={...} /> のように 記事内からだけ 参照するとき用に残している。古い記事に heroImage 行が残っていても無視される。整理するときは行ごと削除してよい。


まとめ

よしあきのブログのサムネイル出力は、「記事を書く → ビルド → /og/{slug}.png が増える」 という一本道だ。手作業は タイトルと公開状態 に集中できる。

デザインを変えたいときは template.ts + キャッシュバージョン が入口。記事ごとに絵を変えたい要件が出たら、テンプレに tags やカテゴリを渡す拡張を検討する段階 — 現状は 全記事同型 で運用コストを下げている。