prelo
Plugins / Open Graph images

Open Graph images

Auto-generated social share images for every page and post.

Agithub.com/anderdigitalMediaprelo >= 0.5Updated Jul 2026Official
Use this plugin
1
2
or point your agent at the spec:
Your agent reads the spec and writes the code into your project. Review the diff like any change.

What this adds

Every page and post gets a proper 1200×630 social share image — site name, title, and your brand color on a clean card — generated automatically. Set a custom image in the SEO fields and it wins; leave it empty and the generated one fills in. Links shared to Slack, LinkedIn or X stop looking like afterthoughts.

How it works

An image route composes an SVG template (title text, wrapped and sized to fit) and rasterizes it to PNG with sharp — which Prelo already uses for media resizing, so no new native dependency. Results are cached to disk keyed by content hash, so each image renders once. The seoTags head helper points og:image at the route when no explicit image is set.

The spec your agent followsexpand ▾
You are adding generated OG images to a Prelo project. Requires sharp (already a CMS dependency) and a `SITE_URL`.

1. **Template.** Create `og-template.js` exporting `renderSvg({ siteName, title, accent })` → an SVG string, 1200×630:
   - Background: near-white or the project's paper color; a 12px accent bar or corner mark in the project's brand color (read it from existing design tokens/config — do not invent one).
   - Site name small at top in the project's font stack (system-safe fallbacks only — SVG rasterization won't load webfonts unless embedded; embed the font as base64 `@font-face` in the SVG only if the project has a local font file).
   - Title large (64–88px), wrapped manually: break into lines of ~20 chars at word boundaries, max 4 lines, ellipsize beyond. Escape XML entities.

2. **Route.** Add to `cms.config.js` `routes:` (custom routes mount under `/api/x/`):

   ```js
   "GET /og/:type/:slug": async (req, res, db) => { /* … */ }
   ```

   (Custom routes are Express routes, so `:type`/`:slug` params work — see `prelo docs api`.) The handler must:
   - Look up the entry (published only) by type + slug; 404 if missing.
   - Compute a cache key `sha256(type + slug + title + updated_at)`; if `data/x-og-cache/{key}.png` exists, stream it.
   - Otherwise `sharp(Buffer.from(svg)).png()` → write to cache, then respond `image/png` with `Cache-Control: public, max-age=31536000, immutable`.
   - Create the cache dir in `hooks.boot`.

3. **Head wiring.** In the frontend's `seoTags`/head helper: `og:image` = the entry's `seo.ogImage` if set, else `{CMS_URL}/api/x/og/{type}/{slug}`, absolute. Add `og:image:width`/`height` (1200/630) and `twitter:card` = `summary_large_image`.

4. **Homepage.** Give the homepage a generated image too (site name + tagline), same route with a reserved slug or a dedicated `"GET /og-home"` route.

5. **Verify.** Fetch the route for one post — confirm a valid PNG, correct dimensions, second fetch served from cache (log or timing). Paste a URL into a share-preview debugger locally (e.g. `curl -s | head` for the meta tags) to confirm og:image resolves absolutely.

After it's built

Nothing to manage. New content gets an image on first share; editing a title invalidates the cache by key. To override any page's image, the client just sets an OG image in Studio's SEO fields.