Written by
tsuka-ryu
At
Sun Jan 11 2026
TakumiでOG Imageを生成した
Takumiを使用してブログにOG画像生成機能を追加しました
あけましておめでとうございます。年末年始寝込んでいたら、もう1月前半が終わってしまいそうです。まだ病み上がりで元気な感じがないので、早く元気になりたいです。今回はブログにOG画像生成機能を追加したので紹介します。
Takumi
Fumadocsのドキュメントに出てきたので、今回はTakumiというライブラリを使ってOG画像を生成してみました。
OG画像生成
OG画像生成といえば、vercelのsatoriと、それをベースにした"next/og"の機能だと思います。自分も過去にsatoriを使って動的OG画像生成をしたことがあります。
ただ、satoriのCSSのサポート状況を眺めながら実装するのは地味に骨が折れた記憶があります。いまはどうなんでしょう。
Takumiの特徴
特徴は公式ドキュメントやリポジトリに記載されてるので詳細は割愛しますが、自分が良さそうだなと思ったのは下記です。
- Rustで実装されているため高速(ファイル数が増えるとビルド時間が長くなりがちな場合に有効)
- Tailwind CSS ファースト
実装
Fumadocsのドキュメント通りに実装するだけです。見た目はTakumiのtemplateからコピペしてちょっと整えました。
fontの読み込みについてもドキュメントに書かれていて、今回はNoto Sans JPを指定してます。ただフォントファイルが9.3MBあるため、サブセット化をしたいと思っています。
// import文は割愛
export async function GET(_req: Request, { params }: RouteContext<"/og/blog/[...slug]">) {
const { slug } = await params;
const page = blog.getPage(slug.slice(0, -1));
if (!page) notFound();
// Load Noto Sans JP font
const fontPath = path.join(process.cwd(), "public/fonts/NotoSansJP.ttf");
const fontData = await fs.readFile(fontPath);
// Load background image directly from filesystem
const backgroundImagePath = path.join(process.cwd(), "public/og-background-image.webp");
const backgroundImageData = await fs.readFile(backgroundImagePath);
const backgroundImageBase64 = `data:image/webp;base64,${backgroundImageData.toString("base64")}`;
// Format date for display
const formattedDate = page.data.date
? new Date(page.data.date).toLocaleDateString("ja-JP", {
year: "numeric",
month: "long",
day: "numeric",
})
: "";
return new ImageResponse(
<BlogPost
title={page.data.title}
author={page.data.author}
date={formattedDate}
// category={""} TODO: カテゴリ実装したら追加
avatar={"https://avatars.githubusercontent.com/u/69495387"}
backgroundImage={`url(${backgroundImageBase64})`}
/>,
{
width: 1200,
height: 630,
format: "webp",
fonts: [
{
name: "Noto Sans JP",
data: fontData,
weight: 400,
style: "normal",
},
],
},
);
}完成
実装で特に苦労することもなく、こんな感じになりました。満足です。

2026/01/17追記
backgroundImageを使っていて、かつ記事数が4つを超えるとビルドが終わらなくなる現象が発生した。
原因の追究は後回しにして、いったんbackgroundImageを使うのをやめた。
Persistent Imagesを適用しようとしたが失敗した。Next.jsの静的ビルド時にfs.readFileでpublicディレクトリを読み込む際のパス解決に問題があるのかもしれない。
最小構成で再現するか調べてみたい。
最終更新: 2026年1月17日