よしあきの Astro 技術ブログでは、記事ごとに サムネイル用 PNG を手で置かない。代わりに ビルド時に Satori で画像を組み立て、次の3か所で 同じ URL を参照する。
| 用途 | 参照元 |
|---|---|
| 記事一覧のカード | BlogPostList.astro |
| 記事ページ上部のヒーロー | BlogPost.astro |
og:image / X のカード画像 | BaseHead.astro |
画像に載る文字は title(記事タイトル)とサイト名 だけ。description はメタタグ用で、画像には描かない。
読み方
出力の流れ
ビルド(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.png(src/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.ts の OG_IMAGE_CACHE_VERSION。テンプレの見た目を変えたあと、SNS やブラウザが古い PNG を掴み続けないための キャッシュ bust 用。
BaseHead.astro では getOgImageAbsoluteUrl で 絶対 URL にし、og:image・twitter:image・og:image:width / height(1200 / 630)を出力する。
デザインと寸法
生成サイズは 1200×630(比率 40:21)。X や Open Graph のリンクプレビューでよく使われる比率に合わせている。
画面上の <img> は同じ比率で縮小表示する。目安は consts.ts の次の定数。
| 定数 | 表示幅 × 高さ | 使う場所 |
|---|---|---|
BLOG_HERO_DISPLAY | 1020 × 536 | 記事ヒーロー |
BLOG_LIST_THUMB_DISPLAY | 720 × 378 | 一覧カード |
配色・レイアウトの正本は src/og/template.ts の createOgTemplate。クリーム系の背景、左のティールのアクセント帯、白いカード内にタイトル、下にサイト名、という フラットな1枚絵 だ。以前、画像生成 AI で1枚ずつ作っていたときのトーンを、コードに固定した。
タイトルが長いときは 80 文字で省略(末尾 …)。一覧と OG で同じ見た目になるので、タイトルは短めに書くか、省略を許容する前提でよい。
記事を書いたあとにやること
新規記事 src/content/blog/2026-06-12.md を追加したら、サムネ用の画像ファイルは不要。やることは次のとおり。
- frontmatter に
title・description・pubDateを書く(スキーマ必須) npm run buildまたはデプロイでビルドする- 本番では
draft: false(またはdraft省略)にして公開
プレビューは開発サーバーで記事 URL を開く。ヒーローと /og/{slug}.png が同じ絵になる。
以前の運用では src/assets/blog/hero/ に PNG を置き、heroImage で frontmatter から参照していた。いまは 表示・OG とも Satori 生成に一本化 している(README も同方針)。
テンプレを変えたとき
src/og/template.tsを編集(色・余白・フォントサイズなど)OG_IMAGE_CACHE_VERSIONを increment(src/consts.ts)- 再ビルド・再デプロイ
renderOgPng.ts を触る必要は、SVG→PNG の変換方式を変えない限りほぼない。フォントを増やすときは fonts.ts と package.json の @fontsource/* を揃える。
依存パッケージは satori・sharp・@fontsource/noto-sans-jp(package.json 参照)。
heroImage について(レガシー)
content.config.ts では heroImage をまだ任意フィールドとして受け付ける。ただし レイアウトのヒーロー・一覧・og:image には使わない。
本文 MDX で <Image src={...} /> のように 記事内からだけ 参照するとき用に残している。古い記事に heroImage 行が残っていても無視される。整理するときは行ごと削除してよい。
まとめ
よしあきのブログのサムネイル出力は、「記事を書く → ビルド → /og/{slug}.png が増える」 という一本道だ。手作業は タイトルと公開状態 に集中できる。
デザインを変えたいときは template.ts + キャッシュバージョン が入口。記事ごとに絵を変えたい要件が出たら、テンプレに tags やカテゴリを渡す拡張を検討する段階 — 現状は 全記事同型 で運用コストを下げている。