DEV Community

GrabShot
GrabShot

Posted on

Automate OG Image Generation for Your Website (No Design Skills Needed)

Every time you share a link on Twitter, Slack, or Discord, the preview image matters. A good OG image gets clicks. A missing or broken one gets scrolled past.

Most solutions require you to design templates in Figma, set up headless Chrome yourself, or use expensive SaaS tools that charge $50+/month. Here's a simpler approach: use a screenshot API to turn any HTML into a perfect OG image.

The Problem

You have a blog, SaaS app, or documentation site. You need unique OG images for every page. Manually creating them is not an option when you have hundreds of URLs.

The Solution: Screenshot API + HTML Template

The idea is simple:

  1. Create an HTML page that renders your OG image (title, description, branding)
  2. Call a screenshot API to capture it as a 1200x630 PNG
  3. Serve that as your og:image

Here's a working example using GrabShot:

curl "https://grabshot.dev/v1/screenshot?url=https://yoursite.com/og/my-post&width=1200&height=630&format=png" \
  -H "x-api-key: YOUR_KEY" \
  -o og-image.png
Enter fullscreen mode Exit fullscreen mode

That's it. One API call, one image.

Step 1: Create an OG Template Page

Create a simple HTML page at /og/:slug on your site:

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      margin: 0;
      width: 1200px;
      height: 630px;
      display: flex;
      flex-direction: column;
      justify-content: center;
      padding: 60px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      font-family: system-ui, sans-serif;
      color: white;
    }
    h1 { font-size: 48px; margin: 0 0 20px; }
    p { font-size: 24px; opacity: 0.9; }
    .logo { margin-top: auto; font-size: 20px; opacity: 0.7; }
  </style>
</head>
<body>
  <h1>Your Post Title Here</h1>
  <p>A brief description that makes people want to click</p>
  <div class="logo">yoursite.com</div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Step 2: Automate with a Build Script

const fetch = require('node-fetch');
const fs = require('fs');

async function generateOG(slug, outputPath) {
  const url = `https://yoursite.com/og/${slug}`;
  const apiUrl = `https://grabshot.dev/v1/screenshot?url=${encodeURIComponent(url)}&width=1200&height=630&format=png`;

  const response = await fetch(apiUrl, {
    headers: { 'x-api-key': process.env.GRABSHOT_KEY }
  });

  const buffer = await response.buffer();
  fs.writeFileSync(outputPath, buffer);
  console.log(`Generated: ${outputPath}`);
}

// Generate for all your posts
const posts = ['intro-to-rust', 'why-typescript', 'docker-tips'];
for (const post of posts) {
  await generateOG(post, `./public/og/${post}.png`);
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Add to Your HTML

<meta property="og:image" content="https://yoursite.com/og/my-post.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
Enter fullscreen mode Exit fullscreen mode

Why Not Just Use Puppeteer Directly?

You could. But then you need to:

  • Maintain a headless Chrome instance
  • Handle memory leaks, crashes, and timeouts
  • Set up a server to run it
  • Deal with fonts, emoji rendering, and platform differences
  • Scale it when you have thousands of pages

A screenshot API handles all of that. You send a URL, you get an image back. The free tier (25 screenshots/month) is enough for most blogs.

Advanced: Dynamic Parameters

You can pass query parameters to your template page and generate images on the fly:

/og?title=My+Post&author=Jane&tag=javascript
Enter fullscreen mode Exit fullscreen mode

Then your template reads the URL params:

const params = new URLSearchParams(window.location.search);
document.querySelector('h1').textContent = params.get('title');
Enter fullscreen mode Exit fullscreen mode

Now every page gets a unique, branded OG image without any manual work.

Real-World Results

Sites with proper OG images see 2-3x higher click-through rates on social media shares. It's one of those small details that compounds over time.

Try It

GrabShot has a free tier with 25 screenshots/month: grabshot.dev

The free screenshot tool lets you test it without signing up.


What approach do you use for OG images? I'd love to hear about other solutions in the comments.

Top comments (0)