Phuc Buiv0.1.0

N-012

Dynamic OG Images on Next.js and Cloudflare Workers

12 Oct 2026 · nextjs, cloudflare

This note as an agent skill: og-images in phucbm/skills.

Shell
npx skills@latest add phucbm/skills --skill og-images

Every page on this site, perxel.com and OpenRace has its own share card: a 1200×630 PNG drawn from the page's data with next/og. All three run on Cloudflare Workers through OpenNext, which has no filesystem, so the usual examples that read a font with fs break there. This note keeps only what surprised me; the step by step version is an agent skill.

The cards

This site's share card for a note: the title in serif on paper, the note's alias above it, the date and the site's version under a rule

This site: a note's card, drawn at build.

Perxel's share card for an insight: a white card with the title, the excerpt, a category pill and a row of tech logos

perxel.com: an insight's card, with its category and the tech it covers.

OpenRace's share card for a race: the race name, a Road pill, the date and the place in Vietnamese

OpenRace: a race's card, drawn on request from live data.

What broke

Three ways to serve them

phucbm.comperxel.comOpenRace
Routeone app/og/[...path]/route.tsxone opengraph-image per page, plain routes for detailsone opengraph-image per page
Drawnat buildat buildon request, with its page's revalidate
Served fromstatic assets, as plain filesOpenNext's cacheOpenNext's cache
FontsTTF files, read at build onlybase64 in .tsbase64 in .ts

This site and Perxel draw every card at build, because all their content is in the repo. OpenRace's races, series and organizers change all the time, so each card revalidates with its page and falls back to the page's own title when the fetch fails: a card route must always return an image.