Open Graph images
Generated 1200×630 social cards for every page — branding, card layers, fonts for non-Latin scripts, and per-page overrides.
Blume can render a 1200×630 social card for every page at build time — no headless browser, thanks to Takumi, so builds stay fast. On by default once deployment.site is set or auto-detected (the og:image URL has to be absolute to be useful to crawlers), and off otherwise or when a default image replaces the cards. Set enabled to override that either way:
seo: {
og: { enabled: true }, // or false to opt out even with a site set
}
Default image
Set seo.og.image to share one image across every page instead of a generated card, or give each locale its own:
seo: {
og: {
image: { en: "/og/default-en.png", fr: "/og/default-fr.png" },
// Or image: "https://example.com/og/default.png"
},
}
A page’s seo.image, or a custom page’s ogImage prop, wins over the default. A locale missing from the map takes the default locale’s image. The default covers custom pages rendered with PageLayout and the generated changelog index too, so Blume stops generating cards once it’s set, unless enabled is true. Root-relative paths resolve under the deployment base. Blume declares image dimensions only for generated cards. Whichever image a page ends up with also goes into its JSON-LD as the page’s image.
Brand the generated card
Set a local SVG and color palette to match the generated card to your brand. The logo can live in public/ or at the project root. Omit any palette value to keep its default.
seo: {
og: {
logo: "/logo/og.svg",
palette: {
accent: "#ff5410",
background: "#1d1d1d",
foreground: "#fff6f2",
muted: "#a6a19f",
border: "#323232",
},
},
}
By default, each card is derived from your content and theme — the page title as the headline, the page description as the subtitle (the same text as its og:description, so seo.description wins over description), and your theme accent for the mark, which shows your site title’s initial when you haven’t set a logo. Images are served at /og/<slug>.png, mirroring each route, and are prerendered as static files even in server mode:
| Page route | Image URL |
|---|---|
/ |
/og/index.png |
/quickstart |
/og/quickstart.png |
/guides/deploy |
/og/guides/deploy.png |
Override the generated card for any page with seo.image — a file in public/ or an external URL. It takes precedence over the generated card and works even when og is off, so you can mix custom images with generated ones:
---
title: Pricing
seo:
image: /og/pricing-custom.png
---
Emoji in a page title or site title render as Twemoji glyphs, fetched from a CDN while the card renders — so a build whose titles contain emoji needs network access. Each glyph is fetched once per build, however many pages use it.
Show, hide, or override card layers
Beyond the headline, the card carries three optional layers: the brand mark in the top-left (your logo, or an accent tile with the site title’s initial), the subtitle under the headline (the page description, or your site description for pages without one), and a footer with your repo slug (from github) and the site’s URL — the deployment site’s host plus deployment.base, so a GitHub Pages project site reads user.github.io/repo. Override any of them with a string of your own, or hide one with false:
seo: {
og: {
site: "docs.acme.com", // footer URL text, or false to hide it
description: false, // hide the subtitle on every card; a string replaces the site fallback
logo: false, // no brand mark at all — not even the initial tile
},
}
Card fonts
By default the card renders in Takumi’s built-in font, which covers only basic Latin glyphs — on its own, a title in another script (Japanese, Chinese, Korean, Arabic, Hindi, Russian, …) would render as tofu, empty boxes.
Every script is covered by default. Behind the built-in font, cards carry a fallback stack of Google Noto families, one per script: Noto Sans for Cyrillic, Greek, Vietnamese, and accented Latin, then Noto Sans JP, Noto Sans Arabic, Noto Sans Devanagari, and so on. A Japanese or Hindi title renders with nothing to configure, even on a site with no locales. Fallback is per glyph, so Latin text keeps the built-in font. A card fetches from Google Fonts only when its text has a glyph the built-in font can’t draw, and then only the subsets those glyphs need: an English card fetches nothing, so a Latin-only site still builds offline. The stack applies unless og.fonts is set, which takes over the whole list.
Chinese, Japanese, and Korean share most Han characters but draw some of them differently, and cards use Japanese forms by default. Each configured locale moves its own family to the front, so a site with a zh locale draws Han in Simplified Chinese forms, and zh-Hant or zh-TW in Traditional ones.
Set theme.fonts and the card follows it. When your config picks its own fonts, the generated cards automatically render the headline in your display font and the description and footer in your body font, so shared links match the site — including non-Latin coverage, with nothing to configure here. (Families from non-Google providers are skipped — the card renderer can only fetch from Google Fonts — but local font files work.)
To use different fonts on cards than on the site, set og.fonts explicitly. It always wins over the theme-derived fonts and replaces the script fallbacks, so list every family your cards need:
seo: {
og: {
fonts: [
"Noto Sans JP",
{ name: "Inter", weight: [400, 700] },
{ name: "Berkeley Mono", src: "./fonts/BerkeleyMono-Regular.woff2" },
],
},
}
Each entry is a Google Fonts family name, an object pinning its weight (a number, a list, or a variable range like "100..900") and style ("normal", "italic", or both), or a local font file — src resolves from the project root, with optional weight and style when the file’s own metadata shouldn’t decide.
Google families are fetched at build — so a build that uses them needs network access — and the renderer only pulls the glyph subsets each title actually uses. Fallback is per-glyph, so adding a family only affects glyphs the other fonts can’t draw.
An explicit og.fonts: [] opts out entirely: cards keep the built-in font even when theme.fonts is set or a title needs a script fallback.
Card cache
Rendered cards are cached on disk between builds, keyed by everything that decides their pixels: the page title and description, the brand text, logo, palette, footer text, and fonts (a local font file by its contents), plus the Blume version. A rebuild renders only the cards whose inputs changed and reads the rest back; the build log reports how many were reused. The cache lives in node_modules/.cache/blume/og; on a platform that keeps node_modules between builds (Vercel and Netlify do, Cloudflare does not) or a CI runner that caches that directory (see Build cache), a deploy that touches a handful of pages re-renders a handful of cards. Cards that no page asked for are removed after each build, so the directory only ever holds the current site’s cards.
Custom page titles
A custom .astro page has no frontmatter to read, so its generated card is titled by humanizing the last URL segment of its route — /getting-started becomes “Getting Started”, but /cli becomes “Cli”. Name those cards explicitly with og.titles, keyed by route ("/" addresses the home, whose card otherwise carries the site title):
seo: {
og: {
titles: {
"/cli": "CLI",
},
},
}
Entries only apply to custom pages — a content page’s card always takes its headline from the page title, so retitle those in frontmatter instead.
seo.image is frontmatter, so it only covers Markdown and MDX content. To give a custom .astro page its own social image — a marketing home or landing page, and the way to give the home page alone a bespoke share image — pass the ogImage prop to PageLayout.