前言 / Introduction

突然想讓自己的文章在分享的時候更酷炫,那就試試看 Open Graph (OG) 圖片,可以讓你的連結在 Discord、Twitter、LinkedIn 等平台上顯示精美的預覽卡片。

本文將教你:/ What you’ll learn:

  • 設計 OG 圖片模板 / Design OG image templates
  • 用 Playwright 自動生成圖片 / Auto-generate images with Playwright
  • 整合 GitHub Actions / Integrate with GitHub Actions
  • 自動化部署到 Cloudflare Pages / Deploy to Cloudflare Pages

Step 1: Design an HTML Template

建立 og-template/template.html 作為 OG 圖片模板。標準 OG 圖片尺寸為 1200x630px

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
  body {
    width: 1200px;
    height: 630px;
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    font-family: 'Inter', -apple-system, sans-serif;
    color: white;
    text-align: center;
    padding: 60px;
  }
  h1 {
    font-size: 80px;
    font-weight: 800;
    margin: 0;
    text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
  }
</style>
</head>
<body>
  <h1>{{TITLE}}</h1>
</body>
</html>

Tips:

  • 使用 1200x630px 標準尺寸
  • 選擇高對比度的背景和文字
  • 保持簡潔,避免過多資訊
  • 使用 {{TITLE}} 作為替換佔位符

Step 2: Generate OG Images with Playwright

建立 scripts/generate_og.js 來自動為所有文章生成 OG 圖片。

Install dependencies:

npm install playwright gray-matter toml
npx playwright install chromium

Generation script:

import fs from "fs";
import path from "path";
import { chromium } from "playwright";
import matter from "gray-matter";
import toml from "toml";

const POSTS_DIR = "./content/posts";
const OUTPUT_DIR = "./static/og";
const TEMPLATE_PATH = "./og-template/template.html";

async function main() {
  // Create output directory if needed
  if (!fs.existsSync(OUTPUT_DIR)) {
    fs.mkdirSync(OUTPUT_DIR, { recursive: true });
  }

  // Launch browser once for all images
  const browser = await chromium.launch();
  const page = await browser.newPage();
  const template = fs.readFileSync(TEMPLATE_PATH, "utf8");

  // Process all markdown files in posts directory
  const files = fs.readdirSync(POSTS_DIR);
  
  for (const file of files) {
    if (!file.endsWith(".md")) continue;

    // Parse front matter (support both TOML and YAML)
    const raw = fs.readFileSync(path.join(POSTS_DIR, file), "utf8");
    let data;
    
    if (raw.startsWith('+++')) {
      // TOML format (Hugo default)
      const parsed = matter(raw, {
        engines: { toml: toml.parse.bind(toml) },
        language: 'toml',
        delimiters: '+++'
      });
      data = parsed.data;
    } else {
      // YAML format
      const parsed = matter(raw);
      data = parsed.data;
    }
    
    const title = data.title || path.basename(file, ".md");
    const slug = path.basename(file, ".md");

    // Generate image
    const html = template.replace("{{TITLE}}", title);
    await page.setContent(html);
    const outPath = `${OUTPUT_DIR}/${slug}.png`;
    await page.screenshot({ path: outPath });
    console.log(`✅ ${outPath}`);
  }

  await browser.close();
}

main();

Run the script:

node scripts/generate_og.js

為什麼需要 GitHub Actions?

雖然可以在本地生成圖片,但 GA 提供了重要的自動化價值:

  • 自動更新 - 修改文章標題時自動重新生成圖片
  • 安全網 - 忘記本地生成時自動補救
  • 團隊協作 - 其他貢獻者不需要安裝 Playwright
  • 保持同步 - 確保圖片永遠和內容一致

建立 .github/workflows/deploy.yml 自動化生成 OG 圖片:

name: Generate OG Images

on:
  push:
    branches:
      - main
    paths:
      - 'content/posts/**'
      - 'og-template/**'
      - 'scripts/generate_og.js'

jobs:
  generate-og-images:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node with npm cache
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Cache Playwright browsers
        uses: actions/cache@v4
        id: playwright-cache
        with:
          path: ~/.cache/ms-playwright
          key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}

      - name: Install Playwright browsers
        if: steps.playwright-cache.outputs.cache-hit != 'true'
        run: npx playwright install --with-deps chromium

      - name: Generate OG images
        run: node scripts/generate_og.js

      - name: Commit and push if changed
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add static/og/
          git diff --quiet && git diff --staged --quiet || \
          (git commit -m "chore: update OG images [skip ci]" && git push)

Key features:

  • 只在文章或模板變更時觸發
  • 自動 commit 生成的圖片
  • 使用 cache 加速 Playwright 安裝

Step 4: Add Front Matter to Posts

在每篇文章的 front matter 加入 images 欄位。PaperMod 主題會自動使用它作為 OG 圖片。

TOML format:

+++
title = 'My Awesome Blog Post'
date = 2025-10-24T12:00:00+08:00
images = ["/og/my-awesome-blog-post.png"]
+++

Bulk add script:

import fs from "fs";
import path from "path";

const POSTS_DIR = "./content/posts";
const files = fs.readdirSync(POSTS_DIR);

for (const file of files) {
  if (!file.endsWith(".md")) continue;
  
  const filePath = path.join(POSTS_DIR, file);
  const basename = path.basename(file, ".md");
  let content = fs.readFileSync(filePath, "utf8");
  
  // Check if images field already exists
  if (content.includes("images =") || content.includes("images:")) {
    continue;
  }
  
  // For TOML format (+++), insert after first +++
  if (content.startsWith("+++")) {
    const lines = content.split("\n");
    lines.splice(1, 0, `images = ["/og/${basename}.png"]`);
    content = lines.join("\n");
    fs.writeFileSync(filePath, content, "utf8");
    console.log(`✅ Updated ${file}`);
  }
}

Step 5: Choose Your Workflow

本地生成 + GitHub Actions 作為保險:

# 日常寫作
vim content/posts/new-post.md

# 本地生成 OG 圖片
node scripts/generate_og.js
# 或使用 Makefile: make og-all

# 提交並推送
git add . && git commit -m "feat: add new post" && git push

好處:

  • ✅ 快速預覽圖片效果
  • ✅ 不需要等待 CI
  • ✅ GA 會檢查並跳過已存在的圖片
  • ✅ 如果忘記生成,GA 會自動補救

Cloudflare Pages 設定

Option A (推薦): GitHub Actions 生成圖片,Cloudflare 只部署:

Build command:

hugo --gc --minify

Option B: Cloudflare 全包(適合沒有 GA 的情況):

Build command:

npm ci && npx playwright install --with-deps chromium && node scripts/generate_og.js && hugo --gc --minify

關於 Pull 的說明

不需要 pull!

workflow 有 git diff 檢查,只有圖片真的改變時才會 commit。所以:

  • 本地已生成圖片 → GA 重新生成 → 沒變化 → 不會 commit → 不需要 pull
  • 本地沒生成圖片 → GA 生成新圖片 → 有變化 → commit → 下次工作前 pull 即可

Bonus: Makefile for Convenience

為了簡化日常操作,建議建立 Makefile

.PHONY: og og-all quick

# Generate OG images for posts
og:
	node scripts/generate_og.js

# Generate all OG images
og-all: og
	node scripts/generate_resume_og.js

# Quick workflow: generate + commit + push
quick:
	@if [ -z "$(MSG)" ]; then \
		echo "Usage: make quick MSG='your message'"; \
		exit 1; \
	fi
	make og-all
	git add .
	git commit -m "$(MSG)"
	git push

使用方式:

make og-all                    # 生成所有 OG 圖片
make quick MSG='add new post'  # 一鍵完成:生成 + commit + push

Tips

  • 尺寸 / Dimensions: 1200x630px
  • 排版 / Layout: 清晰字體,標題醒目,保持邊距 60-80px
  • 配色 / Colors: 高對比度,品牌一致
  • 效能 / Performance: 只在內容變更時重新生成,使用 cache
  • 工作流程 / Workflow: 本地生成為主,GA 作為保險和自動更新機制

Conclusion / 結論

透過這個自動化流程,你可以:

  • 自動為所有文章生成專業的 OG 圖片
  • 提升社群媒體分享的視覺效果
  • 節省手動設計圖片的時間
  • 保持品牌一致性

Good OG images improve first impressions and boost click-through rates on social media.

延伸閱讀 / Further Reading: