初めに
業務でSNSへのシェア機能を実装しました。
要件でユーザごとに投稿するときのOGイメージは一意であることとあったので動的にOG画像を生成する必要がありました。
結構苦戦したので備忘録として実際に Next.js(App Router)で動的に OG 画像を生成した実装例と、そのポイントをまとめます。
OGって何?
OG画像(Open Graph Image) とは、
SNS で URL を貼ったときに表示される “タイトル・説明・画像” を指定する仕組み です。
Web ページの タグで以下のように指定します。
<meta property="og:title" content="記事タイトル">
<meta property="og:description" content="説明文">
<meta property="og:image" content="https://example.com/og.png">
例
Twitterとかでよく出てくるこんなやつ
metaタグのog:imageの画像がSNS上に表示される
今回はこの画像を動的に生成する

どうやって実現したか
Next.jsに機能として提供されていたのでそちらを使いました
Next.js の動的 OG は opengraph-image.tsx をルート直下に置くと、そのルートの OG が生成されます。
今回はユーザごとに異なる画像を出したかったので、
app/
design/
[shareId]/
page.tsx
opengraph-image.tsx
のような構成にしました
具体的な実装
公式に書いてあるとおり以下のような形で実現できます
import { ImageResponse } from "next/og";
export const size = {
width: 1200,
height: 630,
};
export const contentType = "image/png";
export default async function Image({ params }: { params: { slug: string } }) {
const title = decodeURIComponent(params.slug);
return new ImageResponse(
(
<div
style={{
display: "flex",
width: "100%",
height: "100%",
background: "#111",
color: "white",
alignItems: "center",
justifyContent: "center",
fontSize: 48,
fontWeight: 700,
}}
>
{title}
</div>
),
size
);
}
上記のファイル中でAPIへのリクエストも可能です。
私はAPIから必要な情報を取得して、表示情報を組み立てるような実装をしました。
デバッグする時は以下のOG確認サイトを使って表示を見ました
ローカル環境で確認する手順はこちらの記事を参考にしました
ハマったポイント
レイアウト作成
表示をすぐに確認できない+class名を確認できない+使えるCSSに制限がある
という状態で実装に苦労しました。
サポートされているCSSはこちらに記載があります。
Vercelがplayground環境を用意してくれているので最初はそちらで触ってみるのがいいかもしれません
styleの可読性
classが使えないので要素に直接スタイルを書くことになります。
構造がわかりづらかったので対策としてconstで各要素のstyleを管理しました。
// app/share/[userId]/opengraph-image.tsx
import { ImageResponse } from "next/og";
export const size = {
width: 1200,
height: 630,
};
export const contentType = "image/png";
// ⭐ ここ:スタイルを const で分割して可読性UP
const containerStyle = {
display: "flex",
width: "100%",
height: "100%",
background: "#111",
color: "white",
padding: "60px",
flexDirection: "column",
justifyContent: "space-between",
};
const titleStyle = {
fontSize: 56,
fontWeight: 700,
};
const subTextStyle = {
fontSize: 32,
opacity: 0.8,
};
export default async function Image({ params }: { params: { userId: string } }) {
const { userId } = params;
// ダミーで一意の情報を生成
const user = {
name: `User ${userId}`,
followers: Math.floor(Math.random() * 1000),
};
return new ImageResponse(
(
<div style={containerStyle}>
<div style={titleStyle}>{user.name} の投稿</div>
<div style={subTextStyle}>フォロワー:{user.followers} 人</div>
</div>
),
size
);
}
page側でのmetaタグの指定
page側のopenGraphイメージに自分でファイル指定を入れるとうまく動きませんでした。
自分で指定するとNext.js側がよしなにやってくれる設定を上書きしてしまうみたいで、画像が表示されない!!!と困ってました
export async function generateMetadata({ params }) {
const title = decodeURIComponent(params.slug);
return {
title,
description,
///何も指定しなくてもNext.js側が設定してくれるのでOK
//openGraph: {
//},
};
}
日本語フォントが読めない!!!!
デフォルトだと日本語フォントが完全にサポートされていないらしく自分で読み込む必要があります
単純に日本語を出すと「□ □ □ 」と表示されたのでこの対応は必須かもしれません。
こちらの記事に対処法が載っていました
GoogleFontsからフォントをダウンロードしてフォントをサブセット化(必要な文字だけ抜き出す)して軽量化してプロジェクトフォルダに配置しました。
まとめ
クセがありますが簡単な内容ならすぐに動的OGが実現できると思います。
投稿してみたらある程度時間が経たないと画像が表示されない場合がある(Twitter側のクロールタイミングが安定しない?)など、Twitter側の仕様にも悩まされました。
今回はTwitterへの共有機能を作ったので時間がある時に違うSNSへシェアする時はどうすればいいのかも調べてみます