@growth-labs/opengraph
v0.3.0
Published
Generates branded Open Graph images using Cloudflare Browser Rendering (Puppeteer). Screenshots HTML templates, caches in R2, serves via on-demand route. Four sizes per page: 1200×630 (OG / Facebook / LinkedIn / Twitter), 1200×675 (Google Discover 16:9),
Readme
@growth-labs/opengraph
Generates branded Open Graph images using Cloudflare Browser Rendering (Puppeteer). Screenshots HTML templates, caches in R2, serves via on-demand route. Four sizes per page: 1200×630 (OG / Facebook / LinkedIn / Twitter), 1200×675 (Google Discover 16:9), 1200×900 (Discover 4:3), 1200×1200 (Discover 1:1).
Requires: Cloudflare Workers Paid plan (Browser Rendering access).
Config
import opengraph from '@growth-labs/opengraph'
opengraph({
publicDomain: 'media.fedweek.com', // R2 custom domain
r2Binding: 'MEDIA_BUCKET',
browserBinding: 'BROWSER',
brandName: 'FEDweek',
brandColors: {
primary: '#1a365d',
accent: '#e53e3e',
text: '#ffffff',
},
logoUrl: 'https://media.fedweek.com/logos/og-logo.png',
fontFamily: 'Inter',
fontWeight: '600',
route: { enabled: true, regenerateSecret: '...' },
format: 'png',
timeout: 10_000,
})What It Injects
Route: GET /og/[...path].png — on-demand OG image generation. First request renders via Browser Rendering + caches to R2. Subsequent requests served from R2.
Standalone Utilities
import { getOgImageUrl, getOgDiscoverUrl, getOg4x3Url, getOg1x1Url, getOgUrls } from '@growth-labs/opengraph/utils'
import { generateOgImage } from '@growth-labs/opengraph/utils'
getOgImageUrl('media.fedweek.com', '/news/my-article')
// → https://media.fedweek.com/og/news/my-article.png (1200×630)
getOgDiscoverUrl('media.fedweek.com', '/news/my-article')
// → https://media.fedweek.com/og/news/my-article-discover.png (1200×675)
getOg4x3Url('media.fedweek.com', '/news/my-article')
// → https://media.fedweek.com/og/news/my-article-4x3.png (1200×900)
getOg1x1Url('media.fedweek.com', '/news/my-article')
// → https://media.fedweek.com/og/news/my-article-1x1.png (1200×1200)
// All four at once
const { og, discover, discover4x3, discover1x1 } = getOgUrls('media.fedweek.com', '/news/my-article')
// Or generate programmatically (e.g. in publish workflow)
await generateOgImage(pagePath, templateData, { r2, browser }, options)Discover Image Trio
Since 0.3.0 the package captures four variants in a single Puppeteer session:
| Variant | Size | Route suffix | URL helper |
|---|---|---|---|
| OG (default) | 1200×630 | (none) | getOgImageUrl |
| Discover 16:9 | 1200×675 | -discover | getOgDiscoverUrl |
| Discover 4:3 | 1200×900 | -4x3 | getOg4x3Url |
| Discover 1:1 | 1200×1200 | -1x1 | getOg1x1Url |
All four are written to R2 on first generation. invalidateCache() deletes all four atomically.
Custom Templates
Override the built-in template with a function that returns HTML:
opengraph({
template: (data) => `<html><body><h1>${data.title}</h1></body></html>`,
})Template receives: { title, subtitle, description, category, authorName, authorImageUrl, pageType }
Wrangler Bindings
[browser]
binding = "BROWSER"
[[r2_buckets]]
binding = "MEDIA_BUCKET"
bucket_name = "fedweek-public-media"
compatibility_flags = ["nodejs_compat"]Integration Points
- With @growth-labs/seo: Consumer passes OG image URL into
generateMeta(). No direct import between packages. - With @growth-labs/media: Writes to
og/prefix in the same R2 bucket.ogKey()in media utils generates matching keys.
Key Patterns
- Virtual module:
virtual:growth-labs/opengraph/config - R2 key convention:
og/{page-path}.png(aligns with@growth-labs/media/utilsogKey()) ?regenerate=truewith shared secret to force re-render- Browser Rendering has per-account concurrency limits — timeout protects against hangs
