html2canvas Alternative
Searching for an html2canvas alternative usually means one of two things: CSS rendering broke, or you need screenshots on the server. html2canvas cannot do server-side, and its CSS support stopped improving in 2022. Below are six replacements, split into client-side libraries and server-side solutions, with trade-offs for each.
What html2canvas actually does (and why it breaks)
html2canvas is not a screenshot tool in the traditional sense. It reads your page element by element, figures out the styles, and redraws everything onto a <canvas> using its own JavaScript code. Think of it as a second, simpler browser running inside your browser and trying to copy what the real one already rendered.
Back in 2013 when CSS was simpler, that approach worked well enough. Today the gap keeps growing. The library has to support each CSS property individually, and any property the maintainer never added just shows up blank or broken. The last release was January 2022, and there are over 1,400 open issues on GitHub.
These CSS features do not render in html2canvas:
| CSS property | html2canvas status | Chromium status |
|---|---|---|
backdrop-filter |
Not supported | Full support |
clip-path |
Not supported | Full support |
mix-blend-mode |
Not supported | Full support |
box-shadow |
Broken rendering | Full support |
| CSS filters | Partial | Full support |
| 3D transforms | Not supported | Full support |
writing-mode |
Not supported | Full support |
Custom fonts (@font-face) |
Unreliable | Full support |
Then there is the CORS problem. When html2canvas draws a cross-origin image onto canvas, the canvas becomes "tainted" and you can no longer export it. Calling toDataURL() or toBlob() throws a SecurityError. The useCORS: true option helps sometimes, but it depends on every image server sending the right headers. CDNs with redirect chains, S3 buckets without CORS config, SVGs with embedded references, all of these silently kill the export. GitHub issue #3020 has been open for years with no fix.
The library also cannot render iframes, does not support Shadow DOM, and the maintainer has moved on.
Client-side alternatives worth considering
Before jumping to a server-side solution, there are a few client-side libraries that improved on html2canvas since its last release.
html-to-image is the most popular replacement with roughly 3.9 million weekly npm downloads. Instead of painting onto canvas directly, it clones the DOM, inlines all styles, wraps the result in an SVG foreignObject, and draws that SVG to canvas. The output is often more accurate, the API is promise-based and TypeScript-native, and the library is actively maintained. Still client-side though, and still limited by canvas taint rules.
modern-screenshot (about 2 million weekly downloads) is a fork of html-to-image with performance improvements and better support for newer CSS features. Worth trying if html-to-image chokes on your layout.
SnapDOM is the newest option, already pulling around 1 million weekly downloads. It handles Shadow DOM, pseudo-elements, and modern CSS that trips up the older libraries. If you absolutely need to stay client-side, SnapDOM is probably the strongest pick right now.
html2canvas-pro is a community fork of html2canvas itself. It adds clip-path, mix-blend-mode, backdrop-filter, and modern color functions like oklch. Around 1.3 million weekly downloads. A reasonable choice if you have existing html2canvas code and just want to patch the CSS gaps without rewriting everything.
All four share the same wall: they run in the browser. No server-side rendering, no CI/CD pipelines, no background jobs, no batch processing. And cross-origin image restrictions hit all of them equally.
Server-side options: from DIY to managed API
Developers searching for html2canvas server side hit a dead end because the library needs a browser DOM to work. There is no Node.js version, no CLI, no way to run it in a backend process.
On the server, the problems that make html2canvas painful simply go away. A headless Chromium instance loads the page like a normal browser, fetches all resources directly (no CORS), renders all CSS natively, and takes a screenshot. No canvas, no taint errors, no JavaScript re-rendering of CSS properties.
Server-side also opens use cases that client-side capture cannot touch:
- Generating OG images for social media crawlers (they need server-rendered URLs)
- Producing website thumbnails in batch for a directory or gallery
- Capturing pages behind authentication using custom cookies and headers
- Exporting dashboards and reports to PNG or PDF on a schedule
- Running captures in CI/CD for visual regression testing
Puppeteer and Playwright are the self-hosted options. Both give you page.screenshot() backed by real Chromium, full CSS support, no CORS issues. The catch is operational: each Chromium instance eats a few hundred megabytes of memory, Docker images balloon past a gigabyte, and you end up managing browser lifecycle, crashes, leaked processes, and retry logic. I ran into this exact setup on a project that generated social cards, and the infrastructure code ended up longer than the actual feature code. For a deeper look at this trade-off, see the Puppeteer vs screenshot API and Playwright vs screenshot API breakdowns.
A managed screenshot API gives you the same Chromium rendering without the infrastructure. You send an HTTP request with a URL or raw HTML, you get back an image or PDF. ScreenshotRun's HTML to image feature is the direct replacement for what html2canvas tries to do: send HTML in, get an image back. Every CSS property, every web font, every JS framework renders exactly like it would in Chrome.
The pain points that push developers away from html2canvas map directly to API capabilities:
| html2canvas problem | API solution | ScreenshotRun parameter |
|---|---|---|
| CORS errors with external images | Server fetches resources directly | Default behavior, no param needed |
| CSS not rendering (backdrop-filter, clip-path) | Real Chromium renders all CSS | Default behavior |
| Can't capture full page | Full page scroll capture | full_page=true |
| Can't run server-side | HTTP API, any language | GET/POST request |
| No PDF output | PDF with page format control | format=pdf |
| No retina support | Consistent 2x rendering | retina=true |
| iframes not captured | Chromium loads iframes normally | Default behavior |
| Canvas tainted by third-party images | No canvas involved | Default behavior |
Code and feature comparison
A typical html2canvas setup with CORS workarounds:
import html2canvas from 'html2canvas';
const element = document.getElementById('capture-target');
const canvas = await html2canvas(element, {
useCORS: true,
allowTaint: false,
scale: 2,
logging: false,
proxy: '/api/cors-proxy', // your own proxy to fix CORS
});
// This throws SecurityError if any image tainted the canvas
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
const url = URL.createObjectURL(blob);
The same result with a server-side API call in Node.js:
const params = new URLSearchParams({
html: '<div id="capture-target">...your HTML...</div>',
format: 'png',
retina: 'true',
width: '800',
height: '600'
});
const response = await fetch(
`https://api.screenshotrun.com/v1/screenshots/capture?${params}`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const buffer = Buffer.from(await response.arrayBuffer());
For Python backends generating images from HTML templates:
import requests
response = requests.get(
'https://api.screenshotrun.com/v1/screenshots/capture',
params={
'html': '<div>...your HTML template...</div>',
'format': 'png',
'retina': 'true',
'width': '800',
'height': '600'
},
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
with open('capture.png', 'wb') as f:
f.write(response.content)
No CORS proxy, no canvas taint errors, no toBlob workaround. Honestly, the number of hours I have watched developers lose to canvas taint debugging makes this one feel personal. The HTML goes in, a pixel-perfect PNG comes back.
Here is how the two approaches compare across the board:
| Capability | html2canvas | ScreenshotRun API |
|---|---|---|
| Rendering approach | JavaScript DOM reconstruction | Real Chromium engine |
| CSS support | Partial (each property coded manually) | Complete (native Chromium) |
| CORS handling | Canvas taint, proxy required | No CORS issues (server-side fetch) |
| Runs server-side | No (requires browser DOM) | Yes (HTTP API) |
| iframes | Not rendered | Full support |
| Shadow DOM | Not supported | Full support |
| Output formats | Canvas blob (PNG/JPEG) | PNG, JPEG, WebP, AVIF, TIFF, PDF |
| Full page capture | Manual scroll + stitch | full_page=true |
| Device emulation | No | device=mobile, custom viewport |
| Maintenance | Abandoned (last release Jan 2022) | Active, updated Chromium |
| Cost | Free | Free tier (200/month), paid plans after |
When html2canvas is still the right choice
Worth being upfront about this: not every project needs server-side rendering. html2canvas works fine in specific situations and using an API there would be overkill.
If you are building a real-time preview where the user sees their design captured instantly in the browser, client-side capture avoids the 1 to 3 second network round-trip of an API call. Interactive tools like card builders or badge generators benefit from that immediacy.
Privacy-sensitive applications in healthcare or finance may require that data never leaves the browser. With html2canvas, the capture stays client-side. An API means sending HTML content to a third-party server, which might not pass compliance review.
Offline applications and static sites without a backend cannot securely make API calls (the API key would be exposed in client-side code). html2canvas or one of its client-side alternatives is the only option here.
For everything else, if your CSS is complex, your images come from CDNs, you need server-side generation, or you are tired of debugging canvas taint errors, server-side rendering is the more reliable path. Start with ScreenshotRun's free tier and compare the output to what html2canvas produces on the same page. The difference on anything using modern CSS is hard to miss. For the full API parameter reference, check the best screenshot API comparison.
Vitalii Holben