•11 min read

技術ブログのヒーロー画像とOG画像を正気を保ちつつ生成する方法

技術ブログのヒーロー画像とOG画像を正気を保ちつつ生成する方法

私が書くブログ記事はすべて同じ方法で始まりました。記事を書き終えた後、Figmaでヒーロー画像とOG画像を30分かけて作成するのです。記事は完成しているのに、これらの「些細な」タスクがどんどん積み重なっていきました。

聞き覚えがありますか?

限界に達したのは、ブログのブランディングを更新する必要があったときです。色を1つ変更しただけで、突然40以上の画像を再作成しなければならなくなりました。そのとき、このワークフロー全体を自動化することに決めました。

一貫性のあるプロフェッショナルなヒーロー画像とOG画像を自動的に生成する方法をお見せしましょう。

Audio Briefing
0:00 / 0:00

手動での画像作成の問題点

投稿ごとに手動で画像を作成するということは、執筆とデザインの間で常にコンテキストスイッチ(context-switching)が発生することを意味します。Figmaのタブは増え続け、ブログの一貫性は損なわれました。常に「ちょっと調整しているだけ」だったので、投稿ごとにフォント、色、レイアウトが微妙に異なっていました。

さらに悪いことに、私は画像作成を先延ばしにし続けました。下書きフォルダは増える一方でした。Figmaを再び開きたくないという理由だけで、画像なしで記事を公開することもありました。

OG画像が1200x630ピクセルで機能する必要があると言いましたか?それは目測では難しい特定の縦横比です。

Advertisement

私の自動化ソリューション

私はNext.jsのAPIルート、Puppeteer、そしてテンプレート画像を使ってシステムを構築しました。今では画像が必要なときは、投稿のタイトルやその他のパラメータを含むURLを呼び出すだけです。サーバーは完全なHTMLページをレンダリングし、それを画像としてキャプチャし、PNGを返します。

Figmaは不要です。手作業も不要です。関数を呼び出すだけです。

私が作成したコアエンドポイントは次のとおりです。

// app/api/og/route.jsx
import puppeteer from 'puppeteer';

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const title = searchParams.get('title') || 'Untitled Post';
  const category = searchParams.get('category') || 'Tech';

  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();

  await page.setViewport({ width: 1200, height: 630 });
  await page.setContent(renderTemplate({ title, category }));

  const imageBuffer = await page.screenshot({ type: 'png' });
  await browser.close();

  return new Response(imageBuffer, {
    headers: { 'Content-Type': 'image/png' },
  });
}

renderTemplate関数がHTMLを構築します。レイアウトにはCSS GridとFlexboxを使用しています。スタイルは私のブログのデザインシステムと完全に一致しています。

Puppeteerはサーバー上で実際のChromeブラウザを起動します。ホスティング環境がこれをサポートしていることを確認してください。Vercel、Railway、Renderはすべて適切な設定で問題なく動作します。

HTMLテンプレートの構築

私のテンプレートは、デザインされたカードのように見えるようにスタイル設定された単なるHTMLとCSSです。簡略化された構造は次のとおりです。

<div class="og-card">
  <div class="category-badge">{category}</div>
  <h1 class="title">{title}</h1>
  <div class="footer">
    <span class="date">{formattedDate}</span>
    <span class="site">mysite.com</span>
  </div>
</div>

CSSが魔法を起こす場所です。Google Fontsをbase64として埋め込むか、@import経由で読み込んでいます。フォントは最大1つか2つに限定しています。テンプレートがリモートから読み込む必要のあるものが多ければ多いほど、画像生成は不安定になります。

.og-card {
  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
  color: #ffffff;
  padding: 60px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  box-sizing: border-box;
}

グラデーションはシステムによってレンダリングが異なるという痛い経験から学びました。今では、10枚の画像を生成し、さまざまなデバイスで確認することで、常にテンプレートをテストしています。

決定論的にする

誰も教えてくれないことですが、Puppeteerのスクリーンショットはレンダリングのタイミングによってわずかに異なる場合があります。一貫性を確保するために、明示的な待機条件を追加しました。

await page.setContent(renderTemplate({ title, category }));
await page.evaluateHandle('document.fonts.ready'); // Wait for fonts
await page.waitForTimeout(500); // Buffer for rendering
const imageBuffer = await page.screenshot({ type: 'png' });

document.fonts.readyプロミスは、キャプチャする前にWebフォントが読み込まれていることを保証します。その500msのバッファは、テンプレートがトランジションアニメーションを使用している場合の処理を行います。

Advertisement

ヒーロー画像の生成

OG画像は1200x630ピクセルに固定されているため、簡単です。ヒーロー画像は、私のブログではさまざまなサイズで表示されるため、より複雑です。同じシステムを使用しますが、異なる寸法で呼び出します。

私のアプローチは、ビルド時に2〜3つのサイズを生成し、ブラウザがsrcsetを介して適切なものを選択できるようにすることです。

複数のサイズが追加のビルド時間に見合う価値があるか?私にとっては、はい。モバイルの読者は適切なサイズの画像を取得でき、私のLighthouseスコアは高いままでした。

const sizes = [
  { width: 800, height: 450, name: 'small' },
  { width: 1600, height: 900, name: 'large' },
];

for (const size of sizes) {
  await page.setViewport({ width: size.width, height: size.height });
  // capture and save
}

キャッシュ戦略

リクエストごとに画像を生成するのは遅いです。デプロイプラットフォームのエッジキャッシュを使用して、シンプルなキャッシュレイヤーを追加しました。画像URLにはコンテンツのハッシュが含まれているため、同一のリクエストはキャッシュにヒットします。

# Example: cached image URL
https://api.mysite.com/og?title=My%20Post&category=Tech&hash=abc123

テンプレートのデザインを更新するときは、バージョンパラメータを変更してキャッシュを無効にします。古い画像は期限切れになるまで提供され、新しい画像は新しいデザインで生成されます。

結果

このシステムを導入して以来、6ヶ月間ブログの画像のためにFigmaを開いていません。投稿頻度は2倍になりました。すべての画像は同じテンプレートから生成されているため、私のブランドと完全に一致しています。

ビルド時間は投稿あたり8秒増加しました。これは、節約された時間を考えると許容範囲です。

欠点はありますか?もちろん。Puppeteerにバグがあったり、テンプレートにCSSエラーがあったりすると、影響を受けるすべての画像が同時に壊れます。これを軽減するために、更新をデプロイする前にテンプレートを徹底的にテストしています。

はじめに

独自のシステムを構築したいですか?最小限のセットアップは次のとおりです。

  1. 画像生成用のAPIルートを持つNext.jsプロジェクトを作成する
  2. インラインスタイルを持つ静的HTMLテンプレートを構築する
  3. Puppeteerを依存関係として追加する
  4. 実際の投稿タイトルとカテゴリでテストする
  5. 積極的にキャッシュする

テンプレートが最も重要な部分です。すべての投稿で使用することになるので、見た目を整えるのに時間をかけてください。

まとめ

私は複雑だと思っていたので、何年もの間、画像の自動化を避けていました。実際、複雑でした。しかし、その見返りは初期投資を正当化するものでした。今では記事を書き、フロントマターを追加するだけで、画像は自動的に生成されます。

投稿あたり10分以上画像を扱っているなら、これを自動化すべきです。未来のあなたが感謝するでしょう。

まだ手動で行っているワークフロータスクは何ですか?それらのどれかが自動化の候補ですか?

Puppeteer Next.js Automation Blog

こちらもおすすめ

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement