Blog 7 min read

How to Convert HTML to Image: 4 Methods Compared (2026)

Convert HTML to PNG, JPEG, or WebP images using Node.js, Python, or an API. We compare Puppeteer, Playwright, html2canvas, and screenshot APIs.

SnapRender Team
|

How to Convert HTML to Image: 4 Methods Compared

Converting HTML to an image is one of the most common tasks in web development. Whether you're generating social media cards, creating PDF reports, building email templates with dynamic charts, or saving web pages as images, you need a way to render HTML and export it as PNG, JPEG, or WebP.

There are four main approaches, each with different trade-offs. This guide covers all of them with working code examples.

Why Convert HTML to Image?

Common use cases:

  • Social media cards / Open Graph images: Generate og:image previews dynamically for blog posts, products, or user profiles
  • PDF reports: Render HTML templates as images or PDFs for invoices, dashboards, and analytics reports
  • Email content: Embed dynamic charts or styled content that email clients can't render natively
  • Archiving: Save web pages or receipts as images for long-term storage
  • Thumbnails: Generate preview images of websites, documents, or user-submitted content

Method 1: Puppeteer (Node.js)

Puppeteer launches a real Chromium browser, loads your HTML, and screenshots it. This gives you the most accurate rendering because it uses the same engine as Chrome.

Screenshot a URL

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

await page.setViewport({ width: 1200, height: 630 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });

await page.screenshot({ path: 'output.png', type: 'png' });
await browser.close();

Render Raw HTML to Image

You don't need a URL. You can pass HTML directly:

import puppeteer from 'puppeteer';

const html = `
<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      width: 1200px;
      height: 630px;
      margin: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
      color: white;
    }
    h1 { font-size: 48px; text-align: center; padding: 40px; }
  </style>
</head>
<body>
  <h1>How to Convert HTML to Image</h1>
</body>
</html>`;

const browser = await puppeteer.launch();
const page = await browser.newPage();

await page.setContent(html, { waitUntil: 'networkidle0' });
await page.screenshot({ path: 'card.png', type: 'png' });

await browser.close();

Pros and Cons

Pros: Pixel-perfect rendering (real Chrome engine), supports all CSS features including Grid, Flexbox, custom fonts, and animations. Handles JavaScript-rendered content.

Cons: Heavy dependency (~170MB Chromium download), high memory usage (50-300MB per tab), requires careful resource management in production, slow startup time.

Method 2: Playwright (Node.js / Python)

Playwright is Microsoft's alternative to Puppeteer. It supports Chrome, Firefox, and Safari, and has a native Python library.

Node.js

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();

await page.setViewportSize({ width: 1200, height: 630 });
await page.goto('https://example.com');
await page.screenshot({ path: 'output.png' });

await browser.close();

Python

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    page.set_viewport_size({"width": 1200, "height": 630})
    page.goto("https://example.com")
    page.screenshot(path="output.png")

    browser.close()

Render HTML String (Python)

from playwright.sync_api import sync_playwright

html = """
<html>
<body style="background: #1a1a2e; color: white; font-family: sans-serif;
             display: flex; align-items: center; justify-content: center;
             width: 1200px; height: 630px; margin: 0;">
  <h1>Generated with Playwright</h1>
</body>
</html>
"""

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.set_content(html)
    page.screenshot(path="output.png")
    browser.close()

Pros: Multi-browser support, native Python API, similar accuracy to Puppeteer.

Cons: Same resource requirements as Puppeteer, slightly different API conventions.

Method 3: html2canvas (Browser-Side)

html2canvas runs in the browser and converts DOM elements to canvas, then exports as an image. No server required.

<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>

<div id="capture">
  <h1>Hello World</h1>
  <p>This will become an image.</p>
</div>

<script>
html2canvas(document.getElementById('capture')).then(canvas => {
  const link = document.createElement('a');
  link.download = 'screenshot.png';
  link.href = canvas.toDataURL('image/png');
  link.click();
});
</script>

Pros: No server needed, runs in the browser, lightweight.

Cons: Does not use a real rendering engine. It re-implements CSS rendering in JavaScript, so many CSS features are unsupported or render incorrectly (custom fonts, CSS Grid, backdrop-filter, clip-path, gradients, shadows). Cross-origin images are blocked by CORS. Not suitable for pixel-perfect captures.

When to use: Quick client-side captures where accuracy isn't critical (user-facing "save as image" buttons).

When not to use: Generating Open Graph images, PDF reports, or anything that needs to look exactly right.

Method 4: Screenshot API

A screenshot API handles the Chromium infrastructure for you. You send a URL or HTML, and it returns an image.

Using cURL

# Screenshot a URL
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&format=png&width=1200&height=630" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output output.png

Using Node.js

import { SnapRender } from 'snaprender';

const client = new SnapRender('YOUR_API_KEY');

const buffer = await client.screenshot({
  url: 'https://example.com',
  format: 'png',
  width: 1200,
  height: 630
});

// Save to file
import { writeFile } from 'fs/promises';
await writeFile('output.png', buffer);

Using Python

from snaprender import SnapRender

client = SnapRender("YOUR_API_KEY")

image_bytes = client.screenshot(
    url="https://example.com",
    format="png",
    width=1200,
    height=630
)

with open("output.png", "wb") as f:
    f.write(image_bytes)

Pros: No Chromium to install or manage, no memory issues, no crash recovery code, handles SSRF protection, ad blocking, and cookie banners. One API call replaces 30+ lines of code.

Cons: Requires an API key, costs money at scale (though most APIs offer free tiers).

Comparison Table

Method Rendering Accuracy Setup Effort Server Required Best For
Puppeteer Excellent Medium Yes Full control, custom automation
Playwright Excellent Medium Yes Multi-browser, Python projects
html2canvas Poor-Medium Low No Client-side "save as image"
Screenshot API Excellent Minimal No Production workflows, teams

Generating Social Media / Open Graph Images

One of the most popular HTML-to-image use cases is generating dynamic OG images. Here's a practical example:

import { SnapRender } from 'snaprender';

const client = new SnapRender('YOUR_API_KEY');

// Host your OG image template as an HTML page
// e.g., https://yoursite.com/og?title=My+Blog+Post&author=John
const image = await client.screenshot({
  url: 'https://yoursite.com/og?title=My+Blog+Post&author=John',
  format: 'png',
  width: 1200,
  height: 630
});

This approach lets you:

  1. Design your OG image template with HTML/CSS (full creative control)
  2. Generate unique images per page by passing dynamic parameters
  3. Cache the results so each image is only generated once

Performance Tips

If you're generating images at scale, keep these in mind:

  1. Use WebP format when possible. It produces files 25-50% smaller than PNG with similar quality.
  2. Cache aggressively. If the source content hasn't changed, serve the cached image instead of re-rendering.
  3. Set explicit dimensions. Always specify width and height to avoid unnecessary full-page renders.
  4. Use networkidle2 or networkidle0 for waiting. Don't use arbitrary setTimeout delays. They're either too short (content not loaded) or too long (wasted time).

Which Method Should You Use?

Use Puppeteer or Playwright if:

  • You need full control over the browser
  • You're already running a Node.js/Python server
  • You're building a custom automation pipeline
  • You want to avoid third-party dependencies for sensitive content

Use html2canvas if:

  • You need quick client-side captures
  • Rendering accuracy isn't critical
  • You can't use a server-side solution

Use a screenshot API if:

  • You want the simplest possible integration
  • You don't want to manage Chromium infrastructure
  • You need features like ad blocking and cookie banner removal
  • You're building a product where reliability matters more than control

SnapRender offers 500 free screenshots per month, no credit card required. It takes about 30 seconds to get an API key and start converting HTML to images.

Try SnapRender Free

500 free screenshots/month, no credit card required.

Sign up free