DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

HTML to Image API: Convert Any HTML Template to a PNG in One Request

HTML to Image API: Convert Any HTML Template to a PNG in One Request

You need to generate images dynamically. Not screenshots of existing pages — images from HTML templates you control.

Think about it:

  • OG social cards — generate a unique preview image for every blog post, product, or page
  • Email header images — personalized images with user names, dates, or stats
  • Certificates — render diplomas, badges, or achievement cards with dynamic data
  • Dynamic badges — GitHub stats, contributor badges, live metrics as images
  • Invoice thumbnails — preview of what a PDF will look like before sending

You could build this with Puppeteer, but that's overkill. You don't need a full browser. You just need HTML → PNG.

An HTML-to-image API solves this in one request.

The Problem: Building Image Generation In-House

Option 1: Self-hosted Puppeteer

const puppeteer = require('puppeteer');

async function generateImage(htmlString) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // Set viewport for the image size you want
  await page.setViewport({ width: 1200, height: 630 });

  // Set HTML content
  await page.setContent(htmlString);

  // Wait for fonts and images to load
  await page.waitForTimeout(500);

  // Take screenshot
  const screenshot = await page.screenshot({ type: 'png' });

  await browser.close();
  return screenshot;
}
Enter fullscreen mode Exit fullscreen mode

Problems:

  • You own the Chrome process (memory, crashes, updates)
  • Slow (cold starts, process overhead)
  • Hard to scale
  • Can't run on shared hosting or serverless easily
  • 500MB+ Docker images just for image generation

Option 2: HTML-to-Image API (PageBolt)

const response = await fetch('https://api.pagebolt.dev/take_screenshot', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    html: `
      <html>
        <style>
          body {
            width: 1200px;
            height: 630px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-family: Arial;
            margin: 0;
          }
          h1 { font-size: 48px; margin: 0; }
          p { font-size: 24px; margin-top: 10px; }
        </style>
        <body>
          <div>
            <h1>Blog Post Title</h1>
            <p>By Author Name</p>
          </div>
        </body>
      </html>
    `
  })
});

const { imageUrl } = await response.json();
console.log(imageUrl); // Ready to use
Enter fullscreen mode Exit fullscreen mode

Advantages:

  • One HTTP request
  • No Chrome process to manage
  • Instant scaling
  • Works on serverless, shared hosting, anywhere

Real-World Examples

1. Dynamic OG Image Generator

Generate a unique social preview for every blog post:

async function generateOGImage(title, author, date) {
  const html = `
    <html>
      <style>
        body {
          width: 1200px;
          height: 630px;
          background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
          padding: 60px;
          box-sizing: border-box;
          display: flex;
          flex-direction: column;
          justify-content: center;
          color: white;
          font-family: 'Segoe UI', Arial;
        }
        h1 { font-size: 56px; margin: 0 0 20px 0; line-height: 1.2; }
        .meta { font-size: 20px; opacity: 0.9; }
        .logo { font-size: 32px; margin-bottom: 40px; font-weight: bold; }
      </style>
      <body>
        <div class="logo">YourBlog.com</div>
        <h1>${title}</h1>
        <div class="meta">${author} · ${date}</div>
      </body>
    </html>
  `;

  const response = await fetch('https://api.pagebolt.dev/take_screenshot', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ html })
  });

  return (await response.json()).imageUrl;
}

// Usage
const ogImage = await generateOGImage(
  'How to Build Scalable APIs',
  'Jane Developer',
  'Mar 24, 2026'
);
console.log(ogImage); // https://cdn.pagebolt.dev/...
Enter fullscreen mode Exit fullscreen mode

2. Email Preview Image

Generate a preview of what the email will look like:

import requests
import json

def generate_email_preview(name, stats):
    html = f"""
    <html>
      <style>
        body {{
          width: 600px;
          height: 400px;
          background: white;
          padding: 40px;
          box-sizing: border-box;
          font-family: Arial;
          color: #333;
        }}
        h1 {{ font-size: 32px; margin: 0 0 20px 0; }}
        .stats {{
          display: grid;
          grid-template-columns: 1fr 1fr;
          gap: 30px;
          margin-top: 40px;
        }}
        .stat {{
          text-align: center;
          padding: 20px;
          background: #f5f5f5;
          border-radius: 8px;
        }}
        .stat-value {{ font-size: 28px; font-weight: bold; color: #667eea; }}
        .stat-label {{ font-size: 14px; color: #999; margin-top: 5px; }}
      </style>
      <body>
        <h1>Hello {name}!</h1>
        <p>Here's your weekly summary:</p>
        <div class="stats">
          <div class="stat">
            <div class="stat-value">{stats['views']}</div>
            <div class="stat-label">Views</div>
          </div>
          <div class="stat">
            <div class="stat-value">{stats['clicks']}</div>
            <div class="stat-label">Clicks</div>
          </div>
        </div>
      </body>
    </html>
    """

    response = requests.post('https://api.pagebolt.dev/take_screenshot', {
        'headers': {'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json'},
        'json': {'html': html}
    })

    return response.json()['imageUrl']

# Usage
preview_url = generate_email_preview('Jane', {'views': 1234, 'clicks': 567})
print(preview_url)
Enter fullscreen mode Exit fullscreen mode

3. Achievement Badge Renderer

Generate badges for certifications or achievements:

async function generateBadge(achievement, earnedDate) {
  const html = `
    <html>
      <style>
        body {
          width: 300px;
          height: 300px;
          background: radial-gradient(circle, #FFD700 0%, #FFA500 100%);
          display: flex;
          align-items: center;
          justify-content: center;
          margin: 0;
          border-radius: 50%;
        }
        .badge-content {
          text-align: center;
          color: white;
          text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
          filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
        }
        h2 { margin: 0; font-size: 28px; font-family: Arial; }
        p { margin: 5px 0 0 0; font-size: 14px; }
      </style>
      <body>
        <div class="badge-content">
          <h2>🏆</h2>
          <h2>${achievement}</h2>
          <p>${earnedDate}</p>
        </div>
      </body>
    </html>
  `;

  const response = await fetch('https://api.pagebolt.dev/take_screenshot', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ html, width: 300, height: 300 })
  });

  return (await response.json()).imageUrl;
}

// Usage
const badge = await generateBadge('Expert Developer', 'Mar 2026');
Enter fullscreen mode Exit fullscreen mode

Feature Comparison: HTML-to-Image Solutions

Feature Self-Hosted Puppeteer HTML-to-Image API (PageBolt)
Setup Time Hours (install, Docker) Minutes (API key)
Infrastructure Your responsibility Provider handles
Scaling More servers needed Automatic
CSS Support Full modern CSS Full modern CSS
SVG/Fonts ✅ Supported ✅ Supported
Templates You manage Simple HTML strings
Serverless Difficult (500MB+) Perfect fit
Cost (1000 images/month) $50-100 (infra) $10 (API)
Time to First Image 30min–2hrs 5 minutes

Use Case Coverage

Perfect for:

  • OG image generation (blog posts, products, pages)
  • Email header/preview images
  • Certificates and badges
  • Dynamic user-generated content (stats cards, achievement badges)
  • Invoice/receipt previews
  • Live metrics dashboards as images
  • Social media card generation

Not ideal for:

  • Bulk screenshot capture (use screenshot API instead)
  • Page testing (use take_screenshot API instead)
  • Real-time page monitoring (use headless browser directly)

Getting Started

1. Create your HTML template

<html>
  <style>
    body { width: 1200px; height: 630px; /* set your dimensions */ }
  </style>
  <body>
    <!-- Your content here -->
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

2. Send to the API

const response = await fetch('https://api.pagebolt.dev/take_screenshot', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ html: yourHtmlString })
});

const { imageUrl } = await response.json();
Enter fullscreen mode Exit fullscreen mode

3. Use the image URL

Store it, serve it, embed it, whatever your use case needs.

Next Steps

  • Try PageBolt free — 100 requests/month, no credit card. Generate your first OG image.
  • Pick your use case — OG cards, email previews, badges, dynamic images. Start with one.
  • Template library — Build a library of HTML templates you can reuse and customize.

Stop maintaining image generation infrastructure. Start generating images in one API call.


PageBolt: HTML to beautiful images, instantly. Get started free →

Top comments (0)