DEV Community

Diaa elsayed ziada
Diaa elsayed ziada

Posted on

How to Add Dynamic OG Images to Your Developer Blog in 2 Minutes

Your Blog Posts Deserve Better Social Previews

You spend hours writing a technical blog post. You share it on Twitter. And the preview looks like this:

Home Page
yourdomain.com

No image. No description. Just a sad, generic text preview that nobody clicks on.

The fix is Open Graph meta tags — specifically og:image. But if you have 50+ blog posts, you can't open Figma and design a unique image for each one. You need dynamic generation.

Why Puppeteer/Playwright Sucks for Blog OG Images

The "standard" approach is:

  1. Spin up a headless browser in a serverless function
  2. Load an HTML template with your post title
  3. Screenshot it
  4. Serve the PNG

This works in theory. In practice:

  • Cold boot times of 3-5 seconds mean social platforms time out and cache nothing
  • Memory usage of 200-500MB per invocation means expensive serverless bills
  • Font loading is unreliable in headless environments
  • You end up maintaining a mini rendering pipeline for what should be a simple image

A Better Approach: URL-Based Image Generation

What if every blog post got a unique social preview by just adding a URL to your og:image tag?

<meta property="og:image" 
  content="https://socialcard.risero.io/generate?title=Building+a+REST+API+with+Hono&template=blog&api_key=YOUR_KEY" />
Enter fullscreen mode Exit fullscreen mode

That URL returns a 1200×630 PNG. No serverless function in your codebase. No headless browser. No build step. The image is edge-cached for 24 hours so it loads instantly when someone shares your link.

How It Works Under the Hood

Instead of using a browser to render HTML:

  1. Satori (by Vercel) — converts JSX to SVG using a custom layout engine
  2. resvg-wasm — rasterizes SVG to PNG in WebAssembly
  3. Cloudflare Workers — runs at the edge, <100ms response time

Browser-quality rendering without an actual browser.

Framework Integration Examples

Next.js (App Router)

// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
  const post = await getPost(params.slug);
  const ogImage = new URL('https://socialcard.risero.io/generate');
  ogImage.searchParams.set('title', post.title);
  ogImage.searchParams.set('description', post.excerpt);
  ogImage.searchParams.set('template', 'blog');
  ogImage.searchParams.set('api_key', 'YOUR_KEY');

  return {
    openGraph: {
      images: [{ url: ogImage.toString(), width: 1200, height: 630 }],
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

Astro

---
const { title, description } = Astro.props;
const ogUrl = `https://socialcard.risero.io/generate?title=${encodeURIComponent(title)}&template=developer&api_key=YOUR_KEY`;
---
<meta property="og:image" content={ogUrl} />
Enter fullscreen mode Exit fullscreen mode

Hugo

{{ $ogUrl := printf "https://socialcard.risero.io/generate?title=%s&template=blog&api_key=YOUR_KEY" (urlquery .Title) }}
<meta property="og:image" content="{{ $ogUrl }}" />
Enter fullscreen mode Exit fullscreen mode

Ghost (via Code Injection)

<script>
  const title = document.querySelector('meta[property="og:title"]')?.content || document.title;
  const ogMeta = document.createElement('meta');
  ogMeta.setAttribute('property', 'og:image');
  ogMeta.content = `https://socialcard.risero.io/generate?title=${encodeURIComponent(title)}&template=blog&api_key=YOUR_KEY`;
  document.head.appendChild(ogMeta);
</script>
Enter fullscreen mode Exit fullscreen mode

Plain HTML (any static site)

<meta property="og:image" 
  content="https://socialcard.risero.io/generate?title=Welcome+to+My+Blog&template=minimal&api_key=YOUR_KEY" />
Enter fullscreen mode Exit fullscreen mode

Templates Built for Dev Content

The API has 10 templates, but these are the ones built specifically for developer blogs:

  • blog — title, description, author with gradient accent. Perfect for articles.
  • developer — dark theme, monospace font, terminal aesthetics. For dev tool blogs.
  • changelog — version badge with diff-style indicators. For release notes.
  • github — repo card with language, stars, forks. For open source posts.
  • announcement — launch badge with vibrant gradient. For product announcements.

Each supports light/dark themes and custom accent colors.

Get Started in 2 Minutes

  1. Get a free API key at socialcard.risero.io (just your email, no credit card)
  2. Try the live playground to preview templates with your own titles
  3. Copy the URL and drop it in your og:image meta tag
  4. Share a link on Twitter/LinkedIn/Slack and see the preview

Free tier gives you 50 images/day — enough for any personal or team blog.


I built this to solve my own dev blog problem. If you try it, I'd love to hear which templates work best for your content and what's missing.

Top comments (0)