Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

HTML-to-image libraries do not all produce a true browser screenshot. Most client-side packages rebuild an image from the DOM, computed styles, and assets. That can be convenient for an in-browser export, but it may differ from what a user sees. If you need a browser-faithful server-side capture, use a headless browser such as Playwright or Puppeteer, or a managed renderer. Choose after testing your actual fonts, CSS, cross-origin assets, dimensions, and target browsers.

What “HTML to image” means

There are two fundamentally different jobs:

  • DOM reconstruction: a JavaScript library walks an element, reads styles and resources, and paints an approximation to a canvas or SVG. The result is generated inside the page.
  • Browser capture: automation drives a real browser, waits for the page to settle, and captures the rendered surface or an element. This is normally the better match for production screenshots and server-side work.

html2canvas explicitly says its output “may not be 100% accurate to the real representation” because it does not make an actual screenshot; it builds one from information available in the page. That distinction matters for filters, fonts, transforms, pseudo-elements, animations, responsive layout, and anything implemented by a browser feature the library does not support.

How the main approaches compare

Approach Runs in Typical output Best fit Main risk
html2canvas User browser Canvas, then PNG/JPEG or a data URL Client-side export of a known DOM element Unsupported CSS, cross-origin security, and differences from the browser surface
html-to-image User browser PNG, JPEG, SVG, Blob, canvas, or pixel data DOM export with several output helpers and filtering Fidelity still depends on implemented CSS and loaded assets
Playwright or Puppeteer Server, CI, or a worker Browser screenshots Browser-faithful page or element capture Browser binaries, fonts, waits, resource use, and operational maintenance
Hosted rendering API Provider infrastructure Image or PDF, depending on service Teams that do not want to operate browsers API cost, data-handling requirements, and provider-specific limits

No option wins every workload. A library’s documentation does not establish pixel-perfect output for your application; create a fixture from the real page and compare the downloaded files in every required browser.

Using html2canvas in the browser

Install and capture an element

npm install @html2canvas/html2canvas
import html2canvas from '@html2canvas/html2canvas';

const element = document.querySelector('#invoice');
if (!element) throw new Error('Missing #invoice');

await document.fonts.ready;
const canvas = await html2canvas(element, {
  backgroundColor: '#ffffff',
  useCORS: true,
  scale: window.devicePixelRatio
});

const link = document.createElement('a');
link.download = 'invoice.png';
link.href = canvas.toDataURL('image/png');
link.click();

The function returns a Promise. Wait for fonts and any application data before calling it. useCORS can help only when the remote image server sends an appropriate CORS header; it cannot bypass browser security.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

What html2canvas can and cannot read

  • CSS support is finite. The project notes that each CSS property must be implemented individually, so unsupported or partially supported properties can render incorrectly.
  • Images from another origin can taint the canvas. A tainted canvas cannot be exported with toDataURL() or toBlob(). Serve assets with CORS or use a suitable proxy.
  • A cross-origin iframe cannot be read through contentDocument. A sandboxed iframe without allow-same-origin has the same practical limitation.
  • An existing canvas that already contains cross-origin content may taint the new export.
  • Very large canvases can exceed browser- or platform-specific limits and become blank or partly rendered. The limits vary, so test your required dimensions rather than relying on a single published number.
  • It runs in a browser and depends on browser APIs; it is not a Node.js renderer. The project FAQ points server-side users toward Playwright or Puppeteer.

Using html-to-image

Install and export common formats

npm install html-to-image
import { toPng, toJpeg, toSvg, toBlob } from 'html-to-image';

const node = document.getElementById('card');
if (!node) throw new Error('Missing #card');
await document.fonts.ready;

const pngUrl = await toPng(node, { pixelRatio: 2 });
const pngLink = document.createElement('a');
pngLink.download = 'card.png';
pngLink.href = pngUrl;
pngLink.click();

const jpegUrl = await toJpeg(node, { quality: 0.92, pixelRatio: 2 });
const svgUrl = await toSvg(node);
const blob = await toBlob(node);
console.log({ jpegUrl, svgUrl, blob });

The project documents PNG, JPEG, SVG, Blob, canvas, and pixel-data helpers. It also documents a filter option for excluding selected nodes:

const url = await toPng(node, {
  filter: child => !child.classList?.contains('exclude-from-export')
});

Those are package capabilities, not a guarantee that every CSS property, browser, font, or external resource will match your page. Validate the exact content you intend to export.

When a real browser is the better tool

Use browser automation when the requirement is “capture what the browser rendered,” especially for server-side jobs, CI snapshots, authenticated pages, dynamic content, lazy images, or complex CSS. A minimal Playwright example is:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.evaluate(() => document.fonts.ready);
await page.locator('#invoice').screenshot({ path: 'invoice.png' });
await browser.close();

For a full page, replace the locator call with page.screenshot({ path: 'page.png', fullPage: true }). Pin the browser version in CI, install the same fonts in each environment, set a known viewport and device scale, and wait for a selector or application-ready signal rather than assuming a fixed delay is sufficient. Puppeteer follows the same browser-automation strategy; select between them based on your team’s API and operational requirements, not an unverified universal benchmark.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Decision framework for a production implementation

Choose client-side DOM export when

  • The user is already viewing the page and the export can run in that browser.
  • You can accept approximation and have verified the CSS and assets you use.
  • Keeping page data in the user’s browser is important.

Choose a headless browser when

  • The job runs on a server, worker, or CI pipeline.
  • Visual fidelity to a particular browser matters.
  • You need full-page capture, controlled authentication, deterministic viewport settings, or reliable handling of dynamic content.

Choose a hosted renderer when

  • You want to avoid browser installation, patching, and worker management.
  • You can meet the provider’s data, authentication, latency, and cost requirements.
  • You need an API or SDK rather than maintaining capture infrastructure.

Compare candidates on these axes

  • Fidelity: test fonts, filters, transforms, shadows, pseudo-elements, responsive breakpoints, and animations.
  • Runtime: browser-only versus server or CI execution.
  • Security and assets: same-origin and CORS behavior, iframes, cookies, authorization, and tainted canvases.
  • Output: PNG, JPEG, SVG, Blob, raw pixels, or PDF.
  • Scale: viewport, element size, device scale, and browser canvas limits.
  • Operations: browser versions, fonts, waiting conditions, secrets, data handling, and recurring provider charges.

Build a repeatable test fixture

  1. Copy a representative component, including the production fonts, images, SVGs, CSS effects, and dynamic states.
  2. Test same-origin assets, CORS-enabled assets, a blocked asset, and an iframe if your product uses them.
  3. Wait for data, fonts, image decoding, and lazy-loaded content before capture.
  4. Record the requested viewport, element dimensions, device scale, output format, and browser.
  5. Open the resulting files, inspect their pixel dimensions, and compare them on every supported browser.
  6. Include oversized pages and failure cases in automated tests so a blank or partial canvas is detected.

Common failures and fixes

Symptom Likely cause Fix
Export throws a security or tainted-canvas error Cross-origin image or canvas content Serve the asset with CORS, use an approved proxy, or capture with a server browser.
An iframe is blank Cross-origin or sandbox restrictions Capture the frame from its own origin with authorization, or use browser automation at the page level.
Fonts or icons differ Capture started before fonts loaded, or the server lacks the font Await document.fonts.ready; install and pin fonts in server environments.
Shadows, filters, or transforms are missing Library CSS coverage does not include the property or combination Test a supported fallback, simplify the export styles, or use a real browser capture.
Output is blank or clipped Canvas dimension limit or an element with unexpected layout Reduce dimensions, split the capture, verify computed size, and test the target browser and platform.
Dynamic content is absent Capture ran before data, images, or lazy content settled Wait for an application-ready selector, decoded images, and the intended network state.
Node.js import fails html2canvas depends on browser APIs Run it in the browser or switch to Playwright/Puppeteer for server execution.

Or skip the browser setup

ScreenshotNeo is the first service to try when you need a website screenshot API: it removes cookie banners, newsletter popups, and chat widgets before capture, bills only clean shots, and offers a lower paid entry plan than the listed alternatives. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and each response identifies the result with X-Page-Verdict and X-Billed headers.

One GET request returns PNG, JPEG, WebP, or PDF:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the complete parameter list in the ScreenshotNeo documentation. Equivalent Python and Node.js calls:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const buffer = Buffer.from(await res.arrayBuffer());

Its 63 options include full-page capture with lazy images, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, selector or network-idle waits, ad/tracker/request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, an OpenAPI specification, and familiar parameter names for easier migration. An MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

The Free plan includes 1,000 shots each month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is included on every plan. Start with a free ScreenshotNeo account.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

FAQ

Can these libraries capture a complete webpage?

They can capture an element or a page-sized DOM region, but “complete” depends on lazy content, cross-origin resources, canvas limits, and CSS support. A browser screenshot is usually safer for a full-page requirement.

Is SVG automatically more accurate than PNG?

No. SVG output changes the container format; the DOM-to-image implementation still determines which styles and resources are represented. Test the required visual result.

Should I use a fixed timeout before exporting?

A fixed delay can be useful as a last resort, but an application-ready selector, font readiness, image decoding, and a defined network condition are more reproducible.

Frequently Asked Questions

Can html2canvas run in a Node.js script?

Not as a standalone Node renderer; it depends on browser APIs. Use Playwright or Puppeteer for server-side capture.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What should I test before selecting a library?

Test your real fonts, CSS effects, cross-origin assets, iframes, target dimensions, output format, and every browser you support.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.