html2canvas-pro: a screenshot library that keeps up with modern CSS
Screenshots with JavaScript. Next generation!
At a glance
- What is it?
- html2canvas-pro is a fork of html2canvas that adds support for the CSS a browser can now render and the original cannot, including oklch and conic gradients, clip-path, mask-image, mix-blend-mode, backdrop-filter and the filter chain, along with TypeScript types, resource error hooks, progress reporting and cancellation. Its benchmark shows it faster on element-heavy pages and able to render modern colour syntax at all.
- Who is it for?
- Adopt html2canvas-pro if you are rendering DOM to a canvas in the browser and you have hit the point where the original html2canvas either throws on modern colour syntax or silently drops your styling. Do not adopt it if your content is simple and static, because the original library already covers that and the fork carries a larger surface of behaviour to trust.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 10 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A fork aimed at the CSS gap
html2canvas-pro is a fork of niklasvh/html2canvas that includes various fixes and new features, and the readme's framing is that it offers several advantages over the original.
The gap it addresses is specific. html2canvas works by reading the computed styles of your DOM and repainting them into a canvas, which means it has to implement a rendering engine for the parts of CSS the browser already implements. Every feature the browser gained is a feature the library has to be taught, and the original stopped gaining them.
The modern CSS support list is the substance of the project. Colour functions including color() with relative colours, lab(), lch(), oklab() and oklch(), with other functions resolving through the browser's computed styles. background-clip: text. mix-blend-mode and background-blend-mode. object-fit for images. clip-path with inset, rect, xywh, circle, ellipse, polygon and path. writing-mode with horizontal-tb, vertical-rl and vertical-lr. mask-image alpha masks with position, size and repeat. backdrop-filter: blur() for frosted glass captures. The complete gradient family including conic-gradient and the repeating variants. text-emphasis for CJK emphasis marks, along with -webkit-box-reflect, image-set(), border-image-outset and border-image-width. accent-color, outline, -webkit-text-fill-color and isolation. And the filter chain composited for eligible layers, rendered on a dedicated surface with correct layer opacity.
The list also includes fidelity work rather than new syntax. Faithful box-shadow rendering including inset shadows, blur scaling, and shadows through transformed ancestors, plus the image-rendering CSS property with imageSmoothing and imageSmoothingQuality options for pixel-perfect output, and border image, counters and quotes, direction, line-height and transform-origin.
The project is TypeScript, MIT licensed, and the recent releases are v2.5.2 from 2026-10-07, v2.5.1 from 2026-10-04 and v2.5.0 from 2026-09-30, with the last push on 2026-10-13.
The benchmark, and how to read it
The readme publishes a render speed comparison, and the conditions are stated precisely, which is what makes it usable.
The comparison is html2canvas-pro latest against html2canvas 1.4.1, in headless Chromium on an Apple M4, at scale 1, taking the median of 10 interleaved runs, with a benchmark script in the repository and a reproduction command.
The three fixtures give three different pictures. A simple document of about 40 elements took 80 ms in the fork and 87 ms in the original. A large document of about 1200 elements took 295 ms against 468 ms. And a modern CSS page using oklch() and conic-gradient() took 166 ms in the fork, while the original could not render it at all and throws on oklch().
Two things stand out. On a simple document the two are close, which tells you the fork is not paying a large penalty for the extra capability. On a large document the fork is around 1.6 times faster, and that is where the deferred image preloading and the LRU caches for CSS parsing and gradient patterns pay off, because the cost of a large document is dominated by per-element work.
The third row is the real argument for the fork. The performance difference is a modest improvement, but the ability to render a page that uses contemporary colour syntax is the difference between a working screenshot feature and a broken one.
The readme also gives the caveat that matters: absolute times vary by machine, so compare ratios, not milliseconds. And the benchmark is a fixture, not your page, so the honest reading is that the direction of the difference is credible and the magnitude will depend on your document.
The performance work behind the numbers
The performance features are listed as a short set of specific techniques, and they explain the large document result.
Image preloading is deferred, batched and parallel. In a DOM capture, images have to be fetched and decoded before they can be drawn, and doing that one at a time while the main thread is also parsing styles is what makes naive capture slow on image-heavy pages. Batching and parallelising the preload moves that work into the phase where nothing else is happening, and the onProgress hook can report per-batch image preload progress, which is why the readme mentions that specifically.
The CSS parsing cache and the gradient pattern cache are LRU caches. A page with a hundred elements sharing a handful of colours and gradients would otherwise re-parse the same CSS declarations and rebuild the same gradient patterns once per element, and an LRU cache turns that into a lookup. The choice of LRU over a plain map is about bounding memory, which matters because a capture can visit an unbounded number of distinct selectors.
Then there is the native canvas filter fast path with a verified SVG fallback. The filter chain is one of the features the original cannot do at all, and implementing it with the canvas filter API is much faster than emulating each filter function. The fallback exists because the native path is not available in every environment, and the readme is specific that it is verified rather than assumed.
Together with the render speed claim, the readme also lists deferred and batched image preloading under the performance heading, and the developer experience section adds a PerformanceMonitor with per-phase timings, which is how you would find out which phase is slow in your own page rather than guessing.
The API, and the options that actually bite
The usage is one import and one call:
import html2canvas from 'html2canvas-pro';And the basic example appends the canvas to the body:
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});The option that most often causes confusion is the output dimensions. By default the output canvas dimensions are affected by devicePixelRatio, so if you need exact pixel dimensions, for example for a specific file size, you set width, height and scale:
// If you need exact pixel dimensions (e.g., for a specific file size):
html2canvas(element, {
width: 1920,
height: 1080,
scale: 1 // Set scale to 1 for exact dimensions
}).then(canvas => {
// Canvas will be exactly 1920×1080 pixels
const dataURL = canvas.toDataURL('image/png');
});The scale of 1 is the part people miss, since setting width and height while leaving scale at its default still produces a larger canvas than the numbers you gave.
For pixel art there is a dedicated path, with imageSmoothing false to disable anti-aliasing globally and scale 2 to upscale without blur, and the alternative of setting image-rendering: pixelated per element through CSS.
Error handling and cancellation are built in, using an AbortController and a signal option:
const controller = new AbortController();
html2canvas(element, {
useCORS: true,
onError: (error) => {
// Called when a resource (image, font, …) fails to load.
// The render continues — this is a notification hook, not an abort.
console.warn('Resource failed:', error);
},
signal: controller.signal // Rejects with an AbortError when aborted
}).then(canvas => {
document.body.appendChild(canvas);
});
// Cancel an in-progress capture:
controller.abort();The comment in that example is worth reading. onError is a notification hook, not an abort, and the render continues. If you are relying on a failed font load failing the whole capture, that is not what this does.
The package also exposes a browser bundle from a CDN script tag, which sets the global window.html2canvas, and the named exports list beyond the default export includes the Validator, the PerformanceMonitor and the configuration types.
Security, components and the packaging
The developer experience section lists features that matter more in production than the CSS list does.
Security validation is built in through a Validator API, with XSS and SSRF protection. That is a real concern for a screenshot library, because the typical implementation fetches the resources referenced by your DOM, and a page that references an attacker controlled URL is asking the browser to fetch it. A validator that rejects dangerous resource URLs before the fetch is the difference between a screenshot feature and a server side request forgery vector.
The other two are ergonomics with real value. Error, progress and cancellation hooks give onError for failed resources, onProgress for pipeline milestones including per-batch image preload progress, and AbortSignal for cancellation. The onProgress hook matters more than it sounds, because a capture of a large page is a multi-second operation and a progress bar is what makes that acceptable in a user interface.
Then there is Shadow DOM and Web Components support, with slot assignment, shadow-root cloning and automatic iframe placement. Capturing content that lives inside a shadow root is a genuine problem for any DOM to canvas implementation, because the styling context is different and the elements are not reachable by a plain selector. Handling slot assignment and placing the iframe automatically are the details that separate a library that works with web components from one that renders them wrong.
The packaging is modern. The package is ESM first with a CommonJS entry as well, it declares sideEffects as false so a bundler can drop it when unused, and it ships a browser field pointing at the ESM build. The files list is just the dist directory and the licence. The build uses rolldown rather than the older bundler, the test setup is Vitest with a coverage provider and a separate reftest configuration, and the engines field requires Node 24 or newer.
The documentation is a Vitepress site with getting started, configuration, features and FAQ pages, and the repository also carries a demo directory, an examples directory, a docs directory and a demo file for image smoothing.
Editorial conclusion
Adopt html2canvas-pro if you are rendering DOM to a canvas in the browser and you have hit the point where the original html2canvas either throws on modern colour syntax or silently drops your styling. Do not adopt it if your content is simple and static, because the original library already covers that and the fork carries a larger surface of behaviour to trust. Before you commit, check the feature list against the CSS you actually use, and set scale and the width and height options explicitly, because the default output dimensions follow devicePixelRatio and will not match a fixed file size.
Frequently asked questions
What does html2canvas-pro add over the original html2canvas?
It is a fork with support for the CSS a modern browser renders but the original does not, including color() with relative colours, lab(), lch(), oklab(), oklch(), background-clip: text, mix-blend-mode, object-fit, clip-path, writing-mode, mask-image, backdrop-filter blur, the complete gradient family, text-emphasis, and the filter chain, along with TypeScript types, validation, progress and error hooks and cancellation.
Is html2canvas-pro faster than html2canvas?
On the readme's own benchmark, in headless Chromium on an Apple M4 with a median of 10 interleaved runs, a simple document took 80 ms against 87 ms and a large document of about 1200 elements took 295 ms against 468 ms, so it is around 1.6 times faster on element-heavy pages. The readme notes absolute times vary by machine, so compare ratios.
How do I get an exactly sized screenshot with html2canvas-pro?
Pass width, height and scale explicitly, because the default output canvas dimensions are affected by devicePixelRatio. Setting scale to 1 together with width and height gives a canvas of exactly those pixel dimensions.
Can I cancel a capture in html2canvas-pro?
Yes, by passing an AbortSignal through the signal option and calling abort on the controller, which rejects with an AbortError. The onError option is separate and is only a notification hook for a failed resource, since the render continues after it fires.
How is html2canvas-pro installed and used?
Install it with npm install html2canvas-pro, or pnpm or yarn. Then import the default export and call html2canvas(element, options), or load the browser bundle from a CDN script tag, which exposes the global window.html2canvas.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/yorickshan-html2canvas-pro)