Best HTML to Image APIs in 2026: Convert HTML to PNG, JPEG, WebP

Compare the top HTML-to-image APIs and libraries. Client-side, server-side, and cloud-based options for converting HTML to images.

Last updated: 2026-08-24

Try ScreenshotAPI free

200 free screenshots/month. No credit card required.

Start for free

Converting HTML to images is needed for social media cards, certificates, email previews, reports, and dynamic graphics. The challenge is that HTML rendering requires a browser engine for full fidelity. This guide compares every viable approach: client-side libraries, server-side tools, and cloud APIs.

Comparison Table

Tool/APITypeFull CSSJavaScriptFontsFormatsCost
ScreenshotAPICloud APIFullYesYesPNG, JPEG, WebPFree / $19/mo
UrlboxCloud APIFullYesYesPNG, JPEG, WebP, PDF$19/month
ScreenshotOneCloud APIFullYesYesPNG, JPEG, WebP$17/month
html2canvasClient-side libPartialCurrent pagePartialPNG, JPEGFree
dom-to-imageClient-side libPartialCurrent pagePartialPNG, JPEG, SVGFree
SatoriServer-side libLimitedNoManualSVGFree
PuppeteerServer-side toolFullYesYesPNG, JPEG, WebPFree + infra
PlaywrightServer-side toolFullYesYesPNG, JPEGFree + infra

Cloud APIs (Best for Production)

ScreenshotAPI

ScreenshotAPI renders hosted URLs and raw HTML strings with full Chrome. Raw HTML needs no hosting step — pass an html string to POST /api/v1/screenshot, as shown at the end of this guide. To capture a template you already host, pass its URL instead:

javascript
// Host your HTML template, then capture it const templateUrl = `https://yoursite.com/template?title=${encodeURIComponent('My Card')}`; const params = new URLSearchParams({ url: templateUrl, width: '1200', height: '630', type: 'png', waitUntil: 'networkidle2' }); const response = await fetch( `https://screenshotapi.to/api/v1/screenshot?${params}`, { headers: { 'x-api-key': 'sk_live_your_api_key' } } ); const image = Buffer.from(await response.arrayBuffer());

Pros: Full CSS/JS rendering, web fonts, dark mode, all formats, raw HTML input via POST, no infrastructure. Cons: A network round trip per render, and a real browser is slower than a browserless renderer like Satori.

Urlbox

Mature service with built-in HTML rendering endpoint. Supports passing HTML directly in the API request.

Pros: Direct HTML input (no hosting needed), retina rendering, S3 upload. Cons: No free tier, subscription pricing.

ScreenshotOne

Supports custom JavaScript and CSS injection, allowing you to modify the page before capture.

Pros: JS/CSS injection, interactive playground, proxy network. Cons: Monthly subscription, HTML must be at a URL.

Client-Side Libraries

html2canvas

The most popular client-side option. Reconstructs the visible DOM onto a canvas element.

javascript
import html2canvas from 'html2canvas'; const element = document.getElementById('card'); const canvas = await html2canvas(element, { scale: 2, // Retina quality useCORS: true, backgroundColor: null }); canvas.toBlob((blob) => { const url = URL.createObjectURL(blob); // Use the image }, 'image/png');

Pros: No server required, works in the browser, free. Cons: Cannot render external URLs, limited CSS Grid support, font rendering issues, no JS execution context.

dom-to-image

An alternative to html2canvas using SVG foreignObject:

javascript
import domtoimage from 'dom-to-image'; const node = document.getElementById('card'); const dataUrl = await domtoimage.toPng(node, { quality: 1.0 });

Pros: Better SVG support than html2canvas, lighter library. Cons: Same limitations as html2canvas, plus worse cross-browser support.

Server-Side Libraries

Satori (JSX to SVG)

Converts React/JSX components to SVG without a browser:

javascript
import satori from 'satori'; import { Resvg } from '@resvg/resvg-js'; const svg = await satori( <div style={{ display: 'flex', fontSize: 48, color: 'white', background: '#1a1a2e', width: '100%', height: '100%', alignItems: 'center', justifyContent: 'center' }}> Hello World </div>, { width: 1200, height: 630, fonts: [{ name: 'Inter', data: fontData }] } ); const resvg = new Resvg(svg); const png = resvg.render().asPng();

Pros: Extremely fast (<100ms), no browser needed, React component syntax. Cons: Very limited CSS (no Grid, limited Flexbox, no pseudo-elements, no animations), requires manual font loading.

Puppeteer / Playwright

Full browser rendering with page.setContent() for raw HTML:

javascript
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setViewport({ width: 1200, height: 630 }); await page.setContent(htmlString, { waitUntil: 'networkidle0' }); await page.screenshot({ path: 'output.png' }); await browser.close();

Pros: Full CSS/JS rendering, free software, raw HTML input. Cons: Requires Chrome binary (~300 MB), memory-intensive, Docker complexity.

Decision Matrix

Use CaseBest ToolReason
Social media cardsScreenshotAPI or SatoriAPI for full fidelity, Satori for speed
Email previewsScreenshotAPIFull email CSS rendering
Client-side exporthtml2canvasOnly option without a server
Dynamic certificatesScreenshotAPIWeb fonts + complex layouts
OG images (Vercel)@vercel/ogFree, fast, integrated
OG images (general)ScreenshotAPIWorks on any platform
High-volume (10K+/day)Puppeteer + queueCost-effective at scale

CSS Feature Support

CSS Featurehtml2canvasSatoriPuppeteerScreenshotAPI
FlexboxPartialBasicFullFull
GridNoNoFullFull
Web FontsPartialManualFullFull
GradientsYesBasicFullFull
Box ShadowsYesNoFullFull
Pseudo-elementsNoNoFullFull
AnimationsNoNoYesYes
Media queriesNoNoYesYes

Verdict

For production HTML-to-image conversion with full rendering fidelity, ScreenshotAPI or Puppeteer are the best options. ScreenshotAPI removes infrastructure management. Puppeteer is free but requires server management.

For simple, template-based images without complex CSS, Satori is the fastest option.

For client-side only (no server), html2canvas is the only practical choice.

Next Steps

Frequently asked questions

What is the best way to convert HTML to an image?

For full rendering fidelity, use a headless browser (Puppeteer/Playwright) or a cloud API (ScreenshotAPI). For simple layouts without JavaScript, Satori converts JSX to SVG/PNG. Client-side libraries like html2canvas work for basic use cases.

Can html2canvas convert any HTML to an image?

No. html2canvas has significant limitations: it does not support CSS Grid fully, cannot render external iframes, has issues with web fonts, and only works on the current page's DOM (cannot load external URLs).

How do I convert an HTML email template to an image?

Send the markup straight to the API: POST /api/v1/screenshot accepts an html body parameter that renders a raw HTML string, so the template does not have to be hosted anywhere. When html is provided the url parameter is ignored. If the template already lives at a URL, capture that URL instead. Either path renders in Chrome, so email CSS and web fonts resolve the way a browser resolves them.

Is Satori the same as @vercel/og?

@vercel/og uses Satori under the hood. Satori converts React/JSX to SVG, and @vercel/og wraps it in an Edge Function that converts SVG to PNG. You can use Satori directly if you are not on Vercel.

Related resources

Start capturing screenshots today

Create a free account and get 200 free screenshots per month to try the API. No credit card required.

Satori or a render API? Choosing an HTML-to-image path in Node.js

If you want HTML to image in Node without shipping Puppeteer, the two realistic options fail in different places. Satori is a library: it turns JSX into SVG inside your own process with no browser, which is why it is fast and small, but it implements a subset of CSS. A render API runs real Chrome on someone else's machine, so arbitrary HTML and CSS come out the way a browser draws them, at the cost of a network round trip. The deciding question is usually who controls the markup.

What you are renderingBetter fitWhy
JSX you wrote yourself, simple flex layout, high volumeSatoriNo browser runs at all, so it is the cheapest and fastest option at scale
OG images on Vercel from JSX you controlSatori, via @vercel/ogAlready integrated with the platform and runs at the edge
Arbitrary or user-supplied HTMLRender APIYou cannot guarantee unknown markup stays inside Satori's CSS subset
Layouts using CSS Grid, pseudo-elements or animationsRender APISatori does not implement them, so the layout comes out wrong rather than failing loudly
A marketing page or dashboard you already shipRender APIThat markup was written for a browser, not for a CSS subset
HTML email templatesRender APITable-heavy legacy CSS needs a real browser layout engine

Rendering a raw HTML string, with no template hosting

A common misconception about render APIs is that the HTML has to be published somewhere first. It does not. POST /api/v1/screenshot takes an html body parameter, and when it is present the url parameter is ignored, so the markup never leaves your server except as the request body. The official Node SDK exposes it as an ordinary option:

import { ScreenshotAPI } from "screenshotapi-to"
import { writeFile } from "node:fs/promises"

const client = new ScreenshotAPI({ apiKey: process.env.SCREENSHOTAPI_KEY })

const template = `
  <div style="display:flex;align-items:center;justify-content:center;
              height:100%;font-size:64px;background:#1a1a2e;color:#fff">
    Hello World
  </div>
`

// No url and no hosting step: the HTML string itself is rendered in Chrome.
const result = await client.screenshot({
  html: template,
  width: 1200,
  height: 630,
  type: "png",
})

await writeFile("card.png", Buffer.from(result.image))

Can I convert HTML to an image in Node.js without Puppeteer?

Yes, in two different ways. Satori renders JSX to SVG inside your own process with no browser at all, which keeps the deployment small but limits you to the CSS that Satori implements. A render API keeps the browser out of your deployment rather than out of the pipeline: you send the HTML string over HTTP and get PNG, JPEG or WebP back with full Chrome rendering. Neither approach requires a Chrome binary in your own image.

Does ScreenshotAPI need my HTML to be hosted at a public URL?

No. POST /api/v1/screenshot accepts an html body parameter containing the raw HTML string, and when html is provided the url parameter is ignored. Passing the URL of a hosted template is still supported, and it is the better choice when the template already lives on the web and changes independently of your code.

Both paths are documented in the screenshot API reference. The free tier includes 200 renders a month with no credit card.