OG images
Every listing and category page gets its own 1200×630 Open Graph image, rendered during
pnpm build from the listing’s name, tagline, logo and category in your theme’s colours.
seo.ogImages: 'generated' is the default; 'static' points every page at public/og-default.png
instead.
-
Leave
seo.ogImageson'generated', or set it.site.config.ts seo: {ogImages: 'generated',ogScope: { listings: true, categories: true, pseo: false },},pnpm check:configanswerssite.config.ts OK — <name> (<url>); …. -
Regenerate the fallback. It is the image the home page, the tag pages and every failed render use, and the shipped file carries the demo’s name.
Terminal pnpm og:defaultpublic/og-default.png: 1200×630, N bytes — "<site.name>" (<site.tagline>)pnpm og:default --checkrenders to memory and compares; it exits 1 withpublic/og-default.png is out of date — run: pnpm og:defaultwhen the file is stale, which makes it a CI step. -
Build and read the
og:lines.Terminal pnpm buildog: N images to produce (L listings, C categories, 0 pseo, 1 default)og: rendered N images in S s (cache hits H)[zerodirs:og] engine=native, rendered=R, fallbacks=0, logos … / satori … / resvg …; cache=node_modules/.cache/zerodirs/og/ (…)fallbacks=0is the number to look at. -
Open one image:
https://<your domain>/og/<listingBase>/<slug>.png— on the demo,/og/tools/chatgpt.png.
Verify
Section titled “Verify”| Where | You should see | |
|---|---|---|
| The count | the build output | og: N images to produce (…), N being listings plus categories plus one, then fallbacks=0 |
| The image | https://<your domain>/og/<listingBase>/<slug>.png |
a 1200×630 PNG with that listing’s name and logo |
| The tag | view-source:https://<your domain>/<listingBase>/<slug>/ |
<meta property="og:image" content="https://<your domain>/og/<listingBase>/<slug>.png">, with og:image:width 1200 |
| The fallback | pnpm og:default --check |
public/og-default.png is up to date (N bytes, "<site.name>") |
How it works
Section titled “How it works”The images are files, not a runtime. A prerendered endpoint at src/pages/og/ lists every image
the scope calls for and renders each one during the build; nothing renders on request.
| Page | Image | Switch |
|---|---|---|
| a listing | /og/<listingBase>/<slug>.png: logo tile or initial letter, category, name, tagline |
ogScope.listings, on |
| a category, page 1 | /og/<categoryBase>/<slug>.png: name, description, listing count; later pages reuse it |
ogScope.categories, on |
| a generated page | /og/<slug>.png: the h1 and the count |
ogScope.pseo, off |
| everything else: home, indexes, tags, blog, fixed pages | /og/default.png, from site.name and site.description |
always |
Each image is a satori element tree, set in Inter Regular and Bold from src/assets/fonts/og/,
turned into SVG and rasterised to PNG by resvg. The native @resvg/resvg-js addon is the default;
OG_RENDERER=wasm in .env selects @resvg/resvg-wasm, slower but with no binary to load. It is
a build-time variable, read from the environment,
never from wrangler.jsonc. Colours come from the preset’s hex ogColors block, because satori
cannot read oklch(); a hex theme.tokens.primary replaces the preset’s primary, and any other
override is ignored here.
Remote logos are fetched ahead of their render, eight at a time, and SVG logos are rasterised at
256 px before they are embedded. Finished PNGs are cached in node_modules/.cache/zerodirs/og/
under a hash of the template version and every visible field, so a local rebuild only re-renders
what changed. Workers Builds does not keep that directory, so a git-push deploy re-renders every
image on every build. A logo file edited in place keeps its hash; delete the cache directory to
force a render.
When it is off or degraded
Section titled “When it is off or degraded”| Config state | The visitor sees | /admin/ reports |
Fix |
|---|---|---|---|
seo.ogImages: 'static' |
public/og-default.png on every page; the endpoint emits nothing |
nothing | expected; run pnpm og:default so the file carries your name |
| a render fails (satori or resvg throws) | that page’s og:image serves the fallback bytes; the build goes on with [zerodirs:og] <path>: render failed (…); serving public/og-default.png instead and fallbacks=N |
nothing | read the message, fix the cause, rebuild; a failed render is not cached |
| the native addon does not load | the same images, rendered slower, after [zerodirs:og] @resvg/resvg-js could not be loaded (…); falling back to @resvg/resvg-wasm (set OG_RENDERER=wasm to silence) |
nothing | set OG_RENDERER=wasm in .env |
| a font file is missing | every image falls back: render failed (og: font <path> is missing …) per image, fallbacks=N; pnpm og:default refuses to write |
nothing | restore src/assets/fonts/og/ |
public/og-default.png is missing |
under 'static' every og:image is a 404; under 'generated' the first failed render stops the build with og: fallback image <file> is missing (…) |
nothing | pnpm og:default |
seo.ogScope.pseo: true |
one PNG per generated page, each a file against the plan limit | nothing | turn it off first when the budget warns |
| the free edition | the static image everywhere | nothing | the paid starter |
Configuration
Section titled “Configuration”| Field | Default | What it changes |
|---|---|---|
seo.ogImages |
'generated' |
'static' uses public/og-default.png everywhere and emits nothing |
seo.ogScope.listings |
true |
one image per listing |
seo.ogScope.categories |
true |
one image per category |
seo.ogScope.pseo |
false |
one image per generated page |
theme.preset |
'default' |
the ogColors hex block the image uses |
theme.tokens.primary |
unset | a hex value replaces the preset’s primary in the image; an oklch() value does not |
Directorysrc/
Directorypages/og/ the prerendered endpoint: one route per image, the
og:count line- …
Directoryfeatures/paid/og/
- template.ts the satori layout and
OG_TEMPLATE_VERSION - render.ts satori, resvg, the native-to-wasm fallback, the render-failed fallback
- specs.ts which pages get an image and what goes on it
- cache.ts the content-hash cache
- fonts.ts the Inter faces
- template.ts the satori layout and
Directoryassets/fonts/og/ Inter Regular and Bold, TTF
- …
- scripts/og-default.ts
pnpm og:defaultand--check - public/og-default.png the fallback and the static image