HTML Image Node.js April 5, 2026

Convert HTML to Image in Node.js: 5 Methods (2026)

Generate PNG and JPEG images from HTML templates — OG images, social cards, certificates, charts. Five approaches from lightweight to full browser rendering.

Common Use Cases

HTML-to-image conversion powers many features developers build: Open Graph images for social sharing, dynamic email banners, certificate generators, report thumbnails, chart exports, and social media card generators. The approach you choose depends on CSS complexity, performance needs, and whether you're running on a server or serverless.

Method 1: Playwright (Full Rendering)

Playwright renders HTML in a real Chromium instance — pixel-perfect output with full CSS support including flexbox, grid, custom fonts, gradients, and animations (captured as a frame).

const { chromium } = require('playwright');

async function htmlToImage(html, options = {}) {
  const { width = 1200, height = 630, format = 'png', quality } = options;
  const browser = await chromium.launch();
  const page = await browser.newPage({
    viewport: { width, height }
  });

  await page.setContent(html, { waitUntil: 'networkidle' });
  // Wait for web fonts
  await page.evaluateHandle('document.fonts.ready');

  const screenshot = await page.screenshot({
    type: format,
    quality: format === 'jpeg' ? (quality || 90) : undefined,
    fullPage: false
  });

  await browser.close();
  return screenshot; // Buffer
}

// Usage: OG image template
const ogImage = await htmlToImage(`
  <div style="width:1200px;height:630px;background:linear-gradient(135deg,#667eea,#764ba2);
    display:flex;align-items:center;justify-content:center;font-family:sans-serif;">
    <h1 style="color:#fff;font-size:64px;text-align:center;padding:40px;">
      My Blog Post Title
    </h1>
  </div>
`);

Pros: pixel-perfect, full CSS support, web fonts, complex layouts. Cons: ~300-500ms per render, requires Chromium binary (~300MB), heavy for serverless.

Method 2: Puppeteer

Same approach as Playwright but using Google's Puppeteer. The API is similar — choose Puppeteer if you're already using it or prefer Google's ecosystem.

const puppeteer = require('puppeteer');

async function htmlToImagePuppeteer(html, width = 1200, height = 630) {
  const browser = await puppeteer.launch({
    headless: 'new',
    args: ['--no-sandbox', '--disable-dev-shm-usage']
  });
  const page = await browser.newPage();
  await page.setViewport({ width, height, deviceScaleFactor: 2 }); // 2x for retina

  await page.setContent(html, { waitUntil: 'networkidle0' });
  const image = await page.screenshot({ type: 'png', omitBackground: true });

  await browser.close();
  return image;
}

Tip: set deviceScaleFactor: 2 for retina-quality output and omitBackground: true for transparent PNG backgrounds.

Method 3: node-html-to-image

A wrapper around Puppeteer that simplifies the API. Good for quick scripts but adds an abstraction layer with less control.

const nodeHtmlToImage = require('node-html-to-image');

await nodeHtmlToImage({
  output: './social-card.png',
  html: `<html>
    <body style="width:1200px;height:630px;background:#1a1a2e;color:#fff;
      display:flex;align-items:center;justify-content:center;font-family:Arial;">
      <h1>{{title}}</h1>
    </body></html>`,
  content: { title: 'Dynamic Title Here' },
  puppeteerArgs: { args: ['--no-sandbox'] }
});

Supports Handlebars templating (the {{title}} syntax) and batch generation by passing an array of content objects. Under the hood, it's just Puppeteer — so same performance characteristics.

Method 4: Sharp + SVG foreignObject

For simple HTML, you can embed it inside an SVG foreignObject and convert to PNG with Sharp — no browser needed. This is the fastest method (~10-50ms) but has limited CSS support.

const sharp = require('sharp');

function htmlToSvg(html, width = 1200, height = 630) {
  return `
    <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
      <foreignObject width="100%" height="100%">
        <div xmlns="http://www.w3.org/1999/xhtml">
          ${html}
        </div>
      </foreignObject>
    </svg>`;
}

async function htmlToImageFast(html, width, height) {
  const svg = htmlToSvg(html, width, height);
  return sharp(Buffer.from(svg))
    .png()
    .toBuffer();
}

// Simple cards work great
const card = await htmlToImageFast(`
  <div style="width:1200px;height:630px;background:#000;color:#fff;
    padding:60px;font-family:Arial,sans-serif;">
    <h1 style="font-size:48px;margin-bottom:20px;">Hello World</h1>
    <p style="font-size:24px;color:#888;">Generated without a browser</p>
  </div>
`, 1200, 630);

Limitations: no external images (must be base64-inlined), no web fonts (system fonts only), no flexbox/grid in many renderers, no JavaScript. Best for simple text-and-color layouts.

Method 5: SnapAPI (API-Based)

Offload HTML rendering to SnapAPI — send HTML or a URL, get an image back. No browser installation, no memory management, works in serverless and edge functions.

// From URL — capture any live page as an image
const response = await fetch('https://api.snapapi.pics/v1/screenshot', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'sk_live_your_key' },
  body: JSON.stringify({
    url: 'https://mysite.com/og-template?title=Hello+World',
    viewport: { width: 1200, height: 630 },
    fullPage: false,
    format: 'png'
  })
});
const imageBuffer = Buffer.from(await response.arrayBuffer());
# Python — generate OG images at scale
import httpx

def generate_og_image(title, subtitle):
    resp = httpx.post(
        'https://api.snapapi.pics/v1/screenshot',
        headers={'X-Api-Key': 'sk_live_your_key'},
        json={
            'url': f'https://mysite.com/og?title={title}&sub={subtitle}',
            'viewport': {'width': 1200, 'height': 630},
            'format': 'png',
            'blockAds': True,
            'blockCookieBanners': True
        },
        timeout=30
    )
    return resp.content  # PNG bytes

SnapAPI renders with real Chromium, so you get full CSS support (flexbox, grid, custom fonts, gradients) without managing a browser. 200 free requests/month.

Which Method to Choose?

MethodSpeedCSS SupportDependenciesBest For
Playwright300-500msFullChromium (~300MB)Complex layouts, pixel-perfect
Puppeteer300-500msFullChromium (~300MB)Same, Google ecosystem
node-html-to-image300-500msFullPuppeteerQuick scripts, templating
Sharp + SVG10-50msBasicSharp onlySimple cards, high volume
SnapAPI1-3s (network)FullNone (HTTP)Serverless, no infra to manage