2024/02/28
Next.js App Routerで動的にOpenGraphの画像を生成する
本ブログで、動的に画像を生成するための方法を共有します。
はじめに
OpenGraphは、TwitterやDiscord、SlackなどのSNSでリンクを共有した際に、 リンク先のサイトの情報を表示するためのプロトコルです。 QiitaやZennなどはページのタイトルが画像に入っていると思いますが、 今回はそれと同じような画像をNext.jsで動的に生成する方法を紹介します。
やり方
やり方としては、opengraph-imageで生成する方法と、route.tsxを使う方法があります。
今回はroute.tsxを使う方法を紹介します。
普通のルートで生成する方法は、
Next.js公式ドキュメントのこちら
を参照してください。
apiルートを使う場合はNext.js公式ドキュメントにはなく、Vercelの公式ドキュメントに書いてありました。 今回はこちらをもとに進めていきます。
api側の実装
まずは、画像生成するapiルートを作成します。
srcディレクトリを使っていない方は、読み替えてください。
mkdir -p src/app/api/og
touch src/app/api/og/route.tsx公式にはこのように書いてあります。
import { ImageResponse } from 'next/og';
// App router includes @vercel/og.
// No need to install it.
export const runtime = 'edge';
export async function GET() {
return new ImageResponse(
(
<div
style={{
fontSize: 40,
color: 'black',
background: 'white',
width: '100%',
height: '100%',
padding: '50px 200px',
textAlign: 'center',
justifyContent: 'center',
alignItems: 'center',
}}
>
👋 Hello
</div>
),
{
width: 1200,
height: 630,
},
);
}めっちゃ簡単ですね。
ただ、edge runtimeなので、ここではNodeのAPIは使えないことに注意が必要です。
これはroute.tsxを使わなくても同じです。
自分の場合はsearchParamsに文字の情報を渡して実装しました。
また、画像についてはVercelがプレイグラウンドを提供しているので、 自分はそれを使ってスタイリングしました。
https://og-playground.vercel.app/
ただ、フォントは異なるので、注意が必要です。 あとサイズもデフォルトだと800x400なので、各自変更してください。 自分は1200x630にしました。
自分の実装はこんな感じです。
publicディレクトリにフォントファイルを置いて、それをfetchしています。
Next.js v16に上げた段階でエラーがでてしまい確認したところ、 公式ドキュメントでGoogle Fontsをfetchする形に変わっていました。
呼び出し側の実装
今回はホームと各記事の2種類から呼び出そうと思います。
ホーム
まずはホームです。
import type { Metadata } from 'next';
...
export const metadata: Metadata = {
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'),
title: 'RUNFUNRUN.dev',
description: 'This is my tech blog.',
openGraph: {
title: 'RUNFUNRUN.dev',
description: 'This is my tech blog.',
images: '/api/og',
url: '/',
},
twitter: {
title: 'RUNFUNRUN.dev',
description: 'This is my tech blog.',
images: '/api/og',
},
};
...searchParamsを設定してないので、先ほど実装したようにタイトルが ドデカくなります。

記事ページ
記事の方はpage.tsxのgenerateMetadataで設定します。
getPageとgetPagesはFumadocsの機能で、
mdxのデータを取ってきてくれる関数です。
Next.jsのMetadataではmetadataBaseを設定することで、
プロパティ内で相対パスを使うことができます。
import { getPage, getPages } from '@/app/source';
import type { Metadata } from 'next';
...
export const generateMetadata = ({ params }: { params: { slug?: string[] } }) => {
const post = getPage(params.slug);
if (post === undefined) return;
const title = post.data.title;
const description = post.data.description;
const imageParams = new URLSearchParams();
imageParams.set('title', title);
imageParams.set('description', description ?? '');
return {
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'),
title,
description,
openGraph: {
title,
description,
images: `/api/og?${imageParams}`,
url: post.url,
},
twitter: {
title,
description,
images: `/api/og?${imageParams}`,
},
} satisfies Metadata;
};
...記事のページはこんな感じです。

最後に
結構簡単に実装できました。 Next.jsは本当に便利です。
ソースコードを全部見たい方は、本ブログ右上からGitHubリポジトリを 見てみてください。
ではまた。
最終更新日:2026/01/27