# HTML to image with npm: which package actually renders what

> html-to-image, html2canvas, dom-to-image, satori, @vercel/og, node-html-to-image, Puppeteer: how each renders HTML, where it runs and its CSS limits.

Published 2026-11-04 · https://shareglint.com/en/guides/html-to-image-npm

Searching npm for "html to image" returns packages that do three completely different things. **html2canvas, html-to-image and dom-to-image** run in the browser and redraw a DOM node onto a canvas. **Satori and @vercel/og** are layout engines that turn JSX-style markup into SVG without any browser, so they run on the edge. **Puppeteer, Playwright and node-html-to-image** start real Chrome in Node and take a screenshot. Pick by where your code runs and how much CSS you need; the table below shows what each one actually renders.

Versions, release dates and weekly downloads were read from the npm registry and npm's download-count API on 4 November 2026 (downloads for 28 October – 3 November).

## The comparison

| Package | How it renders | Runs in | CSS and limits | Latest release | Weekly downloads |
|---|---|---|---|---|---|
| html2canvas | Re-implements painting on a canvas from computed styles | Browser | Unsupported list includes `filter`, `mix-blend-mode`, `object-fit`; cross-origin images need CORS | 1.4.1, Jan 2022 | 25.5 M |
| html-to-image | DOM clone in an SVG `<foreignObject>`, painted by the browser | Browser | Browser CSS; images and fonts are inlined, and an image blocked by CORS aborts the capture | 1.11.13, Feb 2025 | 11.4 M |
| dom-to-image | Same technique (html-to-image is its fork) | Browser | Same as above | 2.6.0, Oct 2017 | 533 k |
| dom-to-image-more | Maintained fork of dom-to-image | Browser | Same as above | 3.11.0, Sep 2026 | 317 k |
| satori | Own layout engine (Taffy) and text shaping → SVG | Browser, Node, Web Workers | Not complete CSS: no tables, no `@font-face`, no JS; flex, grid and block layout | 0.63.2, Oct 2026 | 6.9 M |
| @vercel/og | Satori 0.33.5 + resvg → PNG | Node, edge (Vercel) | Flexbox only, ttf/otf/woff fonts, 500 KB bundle (Vercel docs) | 1.0.3, Sep 2026 | 2.9 M |
| node-html-to-image | Puppeteer + puppeteer-cluster, Handlebars templates | Node | Real Chrome | 6.2.0, Jul 2026 | 49 k |
| puppeteer | Real Chrome (Chrome for Testing) | Node | Real Chrome | 25.16.0, Oct 2026 | 15.5 M |
| playwright | Real Chromium, Firefox or WebKit | Node | Real browser | 1.64.1, Oct 2026 | 161 M |

Download counts measure popularity, not fit: Playwright's figure is mostly its test runner, and html2canvas is an optional dependency of jsPDF (20.9 M downloads that week), so many installs never call it directly.

## Browser packages: html2canvas, html-to-image, dom-to-image

None of these takes a real screenshot — a web page has no API for that. They rebuild the element:

- [html2canvas](https://html2canvas.hertzen.com/documentation) walks the DOM and draws each box with its own renderer; its [feature page](https://html2canvas.hertzen.com/features) lists the CSS it does not support. It has not had a release since January 2022.
- [html-to-image](https://github.com/bubkoo/html-to-image) and the dom-to-image family clone the node, inline styles, fonts and images as data URLs, wrap it in SVG and let the browser paint it. More CSS survives.

We tested html2canvas 1.4.1 and html-to-image 1.11.13 in Chromium. A cross-origin image without CORS headers was dropped by html2canvas without an exception and made html-to-image's `toPng` reject. With `allowTaint: true`, html2canvas drew the image but `toDataURL` then threw "Tainted canvases may not be exported". html2canvas ignored `filter: invert(1)`, which html-to-image rendered. Neither drew the content of an `<iframe>`.

Use them for "save this chart or receipt as PNG" inside your app. They are a poor fit for images that must be identical for everyone, such as social cards: the result depends on the visitor's browser and installed fonts. Code for both is in the [JavaScript guide](https://shareglint.com/en/guides/html-to-image-javascript).

## Satori and @vercel/og: a layout engine, not a browser

[Satori](https://github.com/vercel/satori) takes React-element-shaped markup, lays it out with its own engine and returns an SVG; you convert that to PNG with resvg or sharp. It needs your font files as data and runs in browsers, Node.js and Web Workers — and, through @vercel/og, in edge functions where Chrome cannot run. This ran on Node 22 with satori 0.63.2 and @resvg/resvg-js:

```js
import satori from 'satori';
import { Resvg } from '@resvg/resvg-js';
import { readFile, writeFile } from 'node:fs/promises';

const font = await readFile('./DejaVuSans.ttf');   // TTF, OTF, WOFF or WOFF2 as a Buffer
const svg = await satori(
  {
    type: 'div',
    props: {
      style: {
        width: '100%', height: '100%', display: 'grid', gridTemplateColumns: '1fr 1fr',
        background: '#0f172a', color: '#f8fafc', fontSize: 56, padding: 80,
      },
      children: [
        { type: 'div', props: { children: 'Left' } },
        { type: 'div', props: { children: 'Right' } },
      ],
    },
  },
  { width: 1200, height: 630, fonts: [{ name: 'DejaVu Sans', data: font, weight: 400, style: 'normal' }] },
);
await writeFile('card.png', new Resvg(svg).render().asPng());
```

With a JSX transform you write the same tree as `<div style={{…}}>`. Satori's README is frank that it is "**not** a complete CSS implementation": `display: table` throws, `@font-face` and `@keyframes` are ignored, and no scripts run. Recent versions added grid and block layout — the grid above rendered as two columns.

`@vercel/og` (and `next/og`, which bundles it) wraps Satori and resvg in an `ImageResponse`. Version 1.0.3 depends on Satori 0.33.5, not the current release, and [Vercel's documentation](https://vercel.com/docs/og-image-generation) lists its limits: flexbox and a subset of CSS (`display: grid` "will not work"), ttf/otf/woff fonts only, and a 500 KB bundle including fonts and images. For a title on a gradient in Next.js it is free and fast; when you hit the limits, see [Next.js OG images without the @vercel/og limits](https://shareglint.com/en/guides/nextjs-og-images-without-vercel-og-limits).

## Puppeteer, Playwright and node-html-to-image: real Chrome

When the HTML must render exactly as in Chrome — your real stylesheet, web fonts, emoji — use a browser. Puppeteer's install downloads Chrome for Testing (about 282 MB on Linux, per its [install guide](https://pptr.dev/guides/installation)); Playwright installs its browsers with `npx playwright install`. node-html-to-image is a convenience wrapper: it depends on puppeteer, puppeteer-cluster and handlebars, takes an `html` string plus `content` for the template, and screenshots `body` or a `selector`.

All three need a server that can run Chrome: memory per page, system libraries and fonts in the container, a timeout per render, and isolation when the HTML comes from users. They do not run in edge functions. A full Puppeteer example with `setContent`, `deviceScaleFactor` and element screenshots is in the [JavaScript guide](https://shareglint.com/en/guides/html-to-image-javascript).

## When an API is simpler than any package

An [HTML to image API](https://shareglint.com/en/html-to-image) is Chrome's rendering behind one `fetch`, which matters in three cases: you need full CSS on the edge or serverless where Chrome cannot run; you need identical output for every visitor, which browser-side packages cannot promise; or you do not want to operate Chrome. With Shareglint you save HTML and CSS once as a template with fields (`POST /v1/templates`), then `POST /v1/images?wait=true` with the values returns the image URL in PNG, JPEG, WebP, PDF, GIF, MP4. No package to install — the request is plain HTTP, shown for Node in the [JavaScript guide](https://shareglint.com/en/guides/html-to-image-javascript).

Be clear about what you give up compared with running Puppeteer: scripts are removed from templates, external URLs inside the HTML and CSS are dropped (images arrive through image fields), and fonts come from the installed list — no uploads. If your HTML is different on every request, a service that takes the whole document each time, such as HTMLCSStoImage, fits better ([comparison](https://shareglint.com/en/alternatives/htmlcsstoimage)). Free accounts get 100 images a month with a watermark; Starter is $9 a month for 2,000 ([pricing](https://shareglint.com/en/pricing)).

## Quick decision

- **User clicks "download as PNG" in your app**: html-to-image; html2canvas if its output suits your CSS.
- **OG images in Next.js, simple layout**: `@vercel/og` / `next/og`.
- **Edge function, your own layout, fonts you ship**: satori + resvg.
- **Server, exact Chrome rendering, you run infrastructure**: puppeteer or playwright (node-html-to-image if you like Handlebars).
- **Server or edge, exact rendering, no Chrome to run**: an API.

Same question in other languages: [Python](https://shareglint.com/en/guides/html-to-image-python), [PHP](https://shareglint.com/en/guides/html-to-image-php), [C#](https://shareglint.com/en/guides/html-to-image-csharp).

## FAQ

### What is the best npm package to convert HTML to an image?

It depends on where the code runs. In the browser, html-to-image (or html2canvas) captures a DOM node. On a server with full CSS support, Puppeteer or Playwright drive real Chrome. On the edge or in Next.js, Satori or @vercel/og render a CSS subset without a browser.

### Is html-to-image better than html2canvas?

They work differently. html2canvas re-implements rendering in JavaScript and skips some CSS (its feature page lists filter, mix-blend-mode, object-fit and others). html-to-image wraps a DOM clone in an SVG foreignObject and lets the browser paint it, so more CSS survives, but a cross-origin image without CORS headers makes the whole capture fail.

### Can Satori render any HTML and CSS?

No. Satori has its own layout engine and its README says it is not a complete CSS implementation: no tables, @font-face is ignored (you pass font files yourself), and no JavaScript runs. Standalone Satori 0.63 supports grid; @vercel/og 1.0.3 pins Satori 0.33.5, and Vercel documents it as flexbox only.

### Which html to image npm package works in Node without Chrome?

Satori (with resvg or sharp to turn its SVG into PNG) and @vercel/og. Every package that gives full CSS support in Node, including node-html-to-image, runs Puppeteer and Chrome underneath.
