Capture guides
html2canvas Alternatives: When to Reach for a Screenshot API Instead
October 6, 2026 · 6 min read · Grabbit Team

If you reached for html2canvas and got a blank box, a missing font, or a thrown error on export, the problem is not your code. html2canvas does not take a screenshot. It re-implements the browser's rendering engine in JavaScript and draws its best guess onto a canvas, so anything it has not reimplemented, like clip-path, CSS filters, cross-origin images, iframes, and late-loading web fonts, comes out wrong or blank.
The right alternative depends on one question: are you capturing a DOM node that already exists in the user's browser, or do you need to render a URL or template on a server? Those are two different jobs, and no single tool does both well.
Why html2canvas has these limits
html2canvas reads the DOM and the computed styles, then paints them onto a <canvas> element using its own renderer written in JavaScript. It never asks the real browser to produce a picture. That design is what makes it work without any backend, and it is also the source of every limit:
- Cross-origin images taint the canvas. If the page draws an image from another origin without permissive CORS headers, the canvas becomes tainted and
toDataURL()throws a security error. You cannot export at all. clip-path, CSS filters,backdrop-filter, and blend modes are often ignored, because the JS renderer has not implemented them.- Iframes and shadow DOM are not traversed the way the real engine traverses them.
- Web fonts render as a fallback if they have not fully loaded at capture time, so text shifts or looks wrong.
The official docs say it plainly: the library is "not a screenshot" and cannot render anything the browser itself has not already painted into a form the script can read. Google's own AI overview for the library lists the same three headline limits: tainted cross-origin canvas, not pixel-perfect, and client-side only.
The alternatives, grouped by the job
| Job | Reach for | Why |
|---|---|---|
| Capture a DOM node the user is viewing | snapdom, html-to-image, dom-to-image-more | Modern in-browser libraries with better font and SVG handling than html2canvas |
| Export a chart, receipt, or card to download | html-to-image | Small API, good canvas output for the in-browser case |
| Render a full page or a URL on a server | A screenshot API (real Chromium) | Uses the browser's own renderer, so cross-origin, fonts, and CSS all work |
| Generate images on a schedule or at scale | A screenshot API | No headless browser to install, patch, or keep alive |
In-browser alternatives (same class of job)
If you genuinely need to capture a node in the user's live browser, including state that only exists after they interact with the page, stay client-side. The strongest options in 2026:
snapdomis a newer library that is fast and handles web fonts and SVG more reliably than html2canvas.html-to-imagerasterizes a DOM node through canvas and SVG with a small, clean API.dom-to-image-more(the maintained fork ofdom-to-image) serializes the node into an SVGforeignObject, which covers some CSS cases html2canvas misses.
These all share html2canvas's fundamental constraint: they reconstruct the node rather than using the real renderer, so complex CSS and cross-origin assets can still break. They swap one set of edge cases for another. For a simple card or chart the user is looking at, they are the right tool. For pixel-accurate output of an arbitrary page, they are not.
When the real fix is a screenshot API
If what you actually need is a picture of a rendered page, not a reconstruction of a DOM node, run the capture server-side in a real headless browser. A hosted screenshot API does exactly this: it loads your URL in actual Chromium, lets the page's CSS, fonts, and JavaScript render the way they do for a human, and returns a hosted image. Because it is a real browser, cross-origin images load normally, clip-path and filters render, and web fonts are fully applied.
The key constraint to understand: a URL-based screenshot API renders a URL, so it cannot screenshot a client-only <div> that never has a URL. The pattern is to host your template at a route (for example /card, with data passed as query parameters) and point the API at it. One template then produces unlimited images, server-side, on demand.
Here is a full-page capture of a rendered URL with Grabbit:
curl https://api.grabbit.live/v1/grabs \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"width": 1280,
"full_page": true,
"format": "webp"
}'
The response includes a hosted image_url you can drop straight into an <img> tag or store. Supported parameters are url, width (320 to 1920), height (240 to 1080), full_page, format (png, jpeg, or webp), delay_ms (0 to 10000, to wait for late content), and selector to capture a single element by CSS selector. To capture just one component instead of the whole page, pass selector the way you would pass a node to html2canvas:
curl https://api.grabbit.live/v1/grabs \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/card", "selector": "#invoice", "format": "png" }'
That is the direct swap for the "screenshot this one element" job, except the element is rendered by a real browser, so the output matches what a user sees.
How to choose
Ask whether the thing you want to capture has a URL.
- No URL, lives only in the browser after user interaction? Use
snapdomorhtml-to-image. A screenshot API cannot reach it. - Has a URL, or you can host the template at one? Use a screenshot API. You get the real renderer and avoid every html2canvas limit, and you do not run Chromium yourself.
Most developers hit html2canvas's wall because they were trying to do the second job with a first-job tool. If your captures are feeding a server-side pipeline, a build step, social cards, or an agent that needs to see a page, the API path removes the whole class of rendering bugs.
Grabbit is a hosted screenshot API: a flat $0.002 per successful live capture, with prepaid annual credits ($50 for 25,000 credits) that do not reset monthly, so unused capture capacity never evaporates. Test-environment keys return a deterministic placeholder image for free, so you can wire up the call before adding a card. It renders and returns a hosted image of a URL. It does not diff images or run your automation, so pair it with your own comparison step if you are doing visual testing.
If you are converting rendered HTML to a file, the sibling guides on rendering HTML to PNG and capturing a specific element cover those exact cases.
FAQ
- What is the best alternative to html2canvas?
- It depends on the job. For capturing a DOM node the user is already looking at, snapdom, html-to-image, and dom-to-image-more are modern in-browser libraries that fix some of html2canvas's font and SVG handling. For capturing a full rendered page or a URL on a server, a hosted screenshot API running real Chromium avoids html2canvas's biggest limits entirely, because it uses the browser's own renderer instead of re-implementing it in JavaScript.
- Why does html2canvas produce blank or wrong images?
- html2canvas does not take a real screenshot. It walks the DOM and re-draws it onto a canvas in JavaScript, so anything it has not implemented is dropped or rendered incorrectly. The common failures are cross-origin images (the canvas is tainted and export throws), CSS clip-path and filters, iframes, shadow DOM, and web fonts that have not fully loaded. These are limits of the approach, not bugs you can configure away.
- Can a screenshot API replace html2canvas?
- Only for a different job. html2canvas captures the user's live, client-side DOM in the browser, including state that exists only after user interaction. A URL-based screenshot API renders a public URL on a server, so it cannot screenshot a client-only div that never has a URL. Use a JS library for in-browser capture; use a screenshot API when you have a URL or a hosted template to render.
- What is the difference between html2canvas and dom-to-image?
- Both are client-side libraries that rasterize a DOM node without a real browser screenshot, so they share the same class of limits. dom-to-image (and the maintained dom-to-image-more fork) builds an SVG with a foreignObject and serializes it, which handles some CSS cases html2canvas misses but struggles with cross-origin assets in its own way. Neither renders a URL on a server.
- How do I capture a page with cross-origin images?
- In the browser, cross-origin images taint the canvas and html2canvas cannot export them unless every asset sends permissive CORS headers. The reliable fix is to render the page server-side with a real headless browser, where cross-origin assets load normally. A screenshot API does this: you pass the URL and it returns a hosted image with the cross-origin content intact.
Capture any website with one API call
Get a free test key and wire your first request in two minutes.
Written by
Grabbit Team
Screenshots as a service
The team behind Grabbit, the screenshot API for developers and AI agents. We write about web capture, rendering, and automating screenshots at scale.
Keep reading

HTML to PNG: How to Render HTML to a PNG Image (URL to PNG via API)
Render HTML to a PNG the reliable way: host your HTML or template at a URL, then capture it with one API call. Why PNG over JPG or WebP, and the honest limits of turning a raw HTML string into an image.
Aug 4, 2026 · 5 min read

How to Screenshot a Specific Element on a Page (With a CSS Selector)
Capture just one element from a web page instead of the whole thing. How element screenshots work in the browser, in code, and with a CSS selector over an API, and when each one fits.
Jun 27, 2026 · 6 min read

How to Convert a URL to an Image (One Call, or a Thousand)
Convert a URL to a hosted PNG, JPG, or WebP with one API call, then scale the same call across a list of URLs. Covers batching, concurrency, caching, and the failure modes that break bulk conversion.
Jul 17, 2026 · 5 min read