Next.js
でブログなどをSSGするときに、OpenGraph
用の画像を自動生成してくれる方法を解説します。
画像生成用のプロジェクトを作成#
まず、画像生成用ののプロジェクトを作成します。もちろん、既存のプロジェクトを使うこともできます。
こちらも Next.js
を使って作成していきます。
セットアップ#
1npx create-next-app og-image
また、今回はスタイリングのために chakra-ui
を使っていきます。
Next.js
プロジェクトに chakra-ui
をインストールする方法はこちらを参考にしてください。
ページを作成する#
1/* pages/index.js */23import { chakra, Heading, HStack } from '@chakra-ui/react';4import { useRouter } from 'next/router';5import Image from 'next/image';67export default function Home() {8 const router = useRouter()9 const searchParams = new URLSearchParams(router.asPath.split(/\?/)[1]);10 const title = searchParams.get('title');1112 return (13 <>14 <chakra.div15 position="relative"16 display="flex"17 flexDirection="column"18 width={1200}19 height={630}20 p={16}21 justifyContent="space-between"22 alignItems="center"23 borderWidth="2rem"24 borderColor="gray.600"25 >26 <div/>27 <Heading px="4rem" textAlign="center" fontSize="5rem">28 {title}29 </Heading>30 <HStack spacing="1rem">31 <chakra.div32 position="relative"33 width="8rem"34 height="8rem"35 borderRadius="50%"36 overflow="hidden"37 >38 <Image src="/profile.png" layout="fill" objectFit="contain" />39 </chakra.div>40 <Heading fontSize="2rem">so99ynoodles</Heading>41 </HStack>42 </chakra.div>43 </>44 );45}
URLの query-string
に title
を渡すことで動的に画像が作れるようにしました。
また、画像のページは1200 x 630
になるようにしました。用途に応じて適宜最適化して使ってください。
localhost:3000/?title=Next.jsでOpenGraph画像を自動生成する
にアクセスすると、以上のような画面ができました。
Vercel
などにデプロイしたら最初のステップは完了です。
ビルド時に上記のページにアクセスし、OpenGraph
画像を取得#
SSGを行う Next.js
プロジェクトでOpenGraph
画像を自動生成する関数を書いていきます。
関数を作成#
1/* utils/openGraphImage.js */23import fs from 'fs';4import { createHash } from 'crypto';5import { chromium } from 'playwright';67export async function getOpenGraphImage(path) {89 /* productionのみで使う */10 if (process.env.NODE_ENV === 'development') {11 return;12 }1314 /* 自分のプロジェクトURLに変更してください */15 const baseUrl = 'https://example.og.com'16 const url = `${baseUrl}${path}`;17 const hash = createHash('md5').update(url).digest('hex');1819 const ogImageDir = `./public/images/og`;20 const imagePath = `${ogImageDir}/${hash}.png`;21 const publicPath = `${process.env.SITE_URL}/images/og/${hash}.png`;2223 try {24 fs.statSync(imagePath);25 return publicPath;26 } catch (e) {27 console.log(`generating og image for ${path}`);28 }2930 const browser = await chromium.launch({ headless: true });31 const page = await browser.newPage();32 await page.setViewportSize({ width: 1200, height: 630 });33 await page.goto(url, { waitUntil: 'networkidle' });34 const buffer = await page.screenshot({ type: 'png' });35 await browser.close();3637 fs.mkdirSync(ogImageDir, { recursive: true });38 fs.writeFileSync(imagePath, buffer);3940 return publicPath;41}
getOpenGraphImage
でやっているを簡単に説明すると以下になります。
playwright
を使ってChrominium
ブラウザーでページにアクセス- ページのスクリーンショットを
/public/images/og
に保存 - 保存された画像のproduction用URLを返す
getStaticProps
で関数を使用#
getOpenGraphImage
関数を getStaticProps
で使い、OpenGraph
画像を自動生成します。
1/* pages/blog/[post].js */23export const getStaticProps = async ({ params }) => {4 const { post } = params;5 const { frontmatter, source } = await getPost(post);6 const openGraphImage = await getOpenGraphImage(`/?title=${frontmatter.title}`);7 return {8 props: {9 frontmatter,10 source,11 openGraphImage,12 }13 };14};
これでビルド時に OpenGraph
画像が自動生成されるようになりました。
Next.js
にSEO情報を適用#
最後に、生成した OpenGraph
画像をSEOに適用させます。
next-seo
を使って簡単に実装できます。
SEOを設定する#
1npm install next-seo
1/* pages/blog/[post].js */2import Head from 'next/head';3import { NextSeo } from 'next-seo';45export default function BlogPost({ frontmatter, source, openGraphImage }) {6 const { title, description, slug } = frontmatter;7 return (8 <Layout>9 <Head>10 <NextSeo11 title={title}12 description={description}13 openGraph={{14 title,15 description,16 url: `${process.env.SITE_URL}/${slug}`,17 images: [{18 url: openGraphImage19 }]20 }}21 />22 </Head>23 {source}24 </Layout>25 )26}
文字化け対策#
playwright
が OpenGraph
画像生成のページに入ると、日本語が文字化けになってしまいました。
対策として日本語に対応したフォントを読み込んで適用させました。簡単に使える Google Fonts
を使用しました。
1/* pages/index.js */23import GoogleFonts from 'next-google-fonts';45export default function Home() {6 ...7 <GoogleFonts href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700;900&display=swap" />8 ...9 <Heading fontFamily="Noto Sans JP" px="4rem" textAlign="center" fontSize="5rem">10 {title}11 </Heading>12 ...13}