TearlineWrap any HTML in one tag and it prints out as a thermal receipt22,766 bytes served, MIT package
Register

Seven ways to turn a page into an image. The register, not a ranking.

the short answer

Every option on the same six criteria, with the URL and the read date on every cell. No order, no score, no winner.

Five of these seven are the same technique with different amounts of code around it. dom-to-image serialises a node into an SVG foreignObject and lets the browser's own layout engine draw it, and html-to-image, dom-to-image-more and modern-screenshot are all forks of that line. The browser can do it with no package at all, and what the packages add is the inlining: fonts, images and stylesheets have to be inside the string before it is serialised, because the SVG data URI has no origin and cannot fetch. SnapDOM takes the same ground and builds a capture object on it, so one capture answers as a PNG, a canvas, a Blob or a download, and its plugins go on to PDF, GIF and video. html2canvas is the one that is genuinely different: it parses your CSS and paints the result itself rather than borrowing the engine, which is why it is 3,379,055 bytes against 186,023 for modern-screenshot, and why it can disagree with what you saw on screen. Tearline is not in the same job at all. It is a custom element that renders a receipt and exports that, so it answers a question about a look, and if your question is how to get a PNG of an element you already have, six of the seven columns here do that and this one does not.

what this page is not

It is not a ranking. Nobody paid to be on it and nobody can, because there is no sponsorship, no affiliate link and no submission route anywhere on this site. Where a project's own page does not carry a figure, the cell says so and names the page that was read and did not carry it, rather than printing a guess.

where the deep pages are

For which package to pick for a given job, read the alternatives ranking. For the technique written out as code you can paste, read the DOM to PNG write-up. For the fork between a browser render and a server render, read the share image comparison.

Side by side

Seven columns. Six criteria, identical for every one.

Each cell is the short answer. Eight columns do not fit a screen, so the grid scrolls sideways to the last three. The same cell in that project's own words is in its section below, and every source is listed at the foot of this page with the day it was read.

CriterionTearlinehtml2canvashtml-to-imagedom-to-image-moremodern-screenshotSnapDOMThe browser on its own
What it producesA receipt, then a PNG of itA canvas elementData URL, SVG, JPEG, Blob, canvas, pixelsSVG, PNG or JPEG, iframes includedPNG, JPEG, WebP, SVG, Blob, canvasSVG, PNG, JPG, WebP, canvas, BlobA Blob or a data URL
What you writeA custom element you wrapA function on a nodeFunctions you importMethods on a domtoimage objectFunctions, or a script tag globalOne function, plus pluginsFour calls you write yourself
What it adds to the install27,017 bytes, 4 files, 0 deps3,379,055 bytes, 391 files, 2 deps315,082 bytes, 82 files, 0 deps976,999 bytes, 126 files, 0 deps186,023 bytes, 12 files, 0 deps565,573 bytes, 6 files, 0 depsNothing, it is in the browser
LicenceMITMITMITMITMITMITNo package, no licence
What it costsFree, nothing is soldFree, nothing is soldFree, nothing is soldFree, nothing is soldFree, nothing is soldFree, nothing is soldFree, ships with the browser
What it does not doRenders a receipt, nothing elseCSS it has not implementedNo Internet ExplorerNo server, no tainted canvasCSS countersNo CommonJS buildYou inline every asset yourself

What you write

Whether you put a component in your markup, or call a function on a node you already have. That decides how much of the result is yours to style, and whether the thing being exported exists on the page before anybody asks for an image of it.

What it does not do

Every entry here runs in the browser, so all of them inherit the tainted canvas rule and none of them can fetch across the network from inside the SVG data URI sandbox. That is the browser's rule and no package changes it. This row carries what each project states on top of it.

Column

html2canvas. A function on a node.

pick it instead of this when

You already ship it and it renders what you expect. It is the one entry on this grid that does not rest on SVG foreignObject at all: it reads the DOM and the computed styles and paints the result onto a canvas itself, so it reaches some cases the foreignObject sandbox cannot. Nothing here says it is broken, only that it is large and has not shipped since January 2022.

what it produces

A canvas element. Its README: the script renders the current page as a canvas image, by reading the DOM and the different styles applied to the elements. The call returns a Promise containing the canvas element, and turning that into a file is left to you.

what you write

A function on a node. The documented call is html2canvas(element[, options]) with a then handler. Nothing goes in your markup, and the element you point it at is one you styled yourself.

Its own page: github.com. On this host: What to use instead of it.

what it adds to the install

3,379,055 bytes, 391 files, 2 deps. Version 1.4.1, published 22 January 2022. It declares css-line-break and text-segmentation, and it is eighteen times the size of the smallest package on this grid. The size follows from the approach: reimplementing layout needs the line breaking and text segmentation the browser would otherwise have done.

licence, and what it costs

MIT. The registry carries license MIT on version 1.4.1.

Free, nothing is sold. The repository page carries the licence as MIT license. There is no hosted tier, no account and no paid edition.

what it does not do

CSS it has not implemented. Its README: as each CSS property needs to be manually built to be supported, there are a number of properties that are not yet supported. The same README describes the script as being in a very experimental state. That is the failure mode to expect from this one: the page looks right, the canvas does not, and nothing throws.

Column

html-to-image. Functions you import.

pick it instead of this when

You are replacing dom-to-image in code that already exists. It is the fork with the closest API to the original, so the migration is mostly an import path, and it returns more shapes than anything else here: a data URL, an SVG, a JPEG, a Blob, a canvas, or raw pixel data you can read one pixel at a time.

what it produces

Data URL, SVG, JPEG, Blob, canvas, pixels. toPng, toSvg, toJpeg, toBlob, toCanvas and toPixelData are the six top level functions. toPixelData hands back a Uint8Array where every four elements are one pixel's RGBA, which no other entry on this grid offers.

what you write

Functions you import. Its own line: generates an image from a DOM node using HTML5 canvas and SVG. You import the functions and pass a node you already have, and the README shows the React form as a ref plus a click handler.

Its own page: github.com. On this host: Where it sits against the rest.

what it adds to the install

315,082 bytes, 82 files, 0 deps. Version 1.11.13, published 14 February 2025, with an empty dependencies object. It is the second oldest release on this grid after html2canvas.

licence, and what it costs

MIT. The registry carries license MIT on version 1.11.13.

Free, nothing is sold. The repository page carries the licence as MIT license. There is no hosted tier and no account.

what it does not do

No Internet Explorer. Its README: Internet Explorer is not (and will not be) supported, as it does not support SVG foreignObject tag. It names Promise and SVG foreignObject as the two things the browser has to have, and records that Chrome performs significantly better than Firefox and Safari on big DOM trees.

Column

dom-to-image-more. Methods on a domtoimage object.

pick it instead of this when

You want to know which fetch failed, and you want to do something about it. It is the only entry here that publishes hooks for the inlining step: requestInterceptor to supply or recover any external resource, loadExternalStyleSheet to opt into cross-origin stylesheets so their font-face rules are found, adjustPseudoElement, preserveScroll, and onImageError as a callback for every resource that could not be fetched. If an export is coming out half drawn, that is the row of features that tells you why.

what it produces

SVG, PNG or JPEG, iframes included. Its README: a library which can turn arbitrary DOM node, including same origin and blob iframes, into a vector (SVG) or raster (PNG or JPEG) image. The iframe case is stated by this project and by no other project on this grid.

what you write

Methods on a domtoimage object. domtoimage.toPng(node, options), with the options object carrying the hooks. It is a fork of dom-to-image by Anatolii Saienko, which is the original foreignObject implementation every other browser side entry here descends from.

Its own page: github.com.

what it adds to the install

976,999 bytes, 126 files, 0 deps. Version 3.10.2, published 10 July 2026, with an empty dependencies object. It is five times the size of modern-screenshot, and the features listed in its own changelog are where that goes.

licence, and what it costs

MIT. The registry carries license MIT on version 3.10.2, and the README's last line reads License, MIT. The repository page read did not carry the detected MIT license label that the other four repositories here show in their sidebar, so the registry is the source quoted for this cell.

Free, nothing is sold. The repository has no hosted tier, no account and nothing to buy. It moved back from the 1904labs organisation to the maintainer's own repositories on 10 July 2026, at version 3.10.1.

what it does not do

No server, no tainted canvas. Its Things to watch out for section: a canvas element inside the node is handled unless the canvas is tainted, in which case rendering will rather not succeed; a link stylesheet added in the same tick as the call may not be in the CSSOM yet, so its font-face rules are never found and the glyph is missing; and a render call rejects with a browser DOM is required (SSR) error where there is no document.

Column

modern-screenshot. Functions, or a script tag global.

pick it instead of this when

You want the smallest of these, or you are taking more than one capture. It is 186,023 bytes across 12 files, the lightest package on this grid, and it is the only one that publishes a reusable context and a web worker so repeated captures do not redo the same work. It also exposes domToForeignObjectSvg, which hands you the SVG itself.

what it produces

PNG, JPEG, WebP, SVG, Blob, canvas. domToPng, domToSvg, domToJpeg, domToWebp, domToDataUrl, domToBlob, domToPixel, domToImage, domToCanvas and domToForeignObjectSvg. The last one returns the SVG foreignObject before it is rasterised, which no other entry here exposes.

what you write

Functions, or a script tag global. Its own line: quickly generate image from DOM node using HTML5 canvas and SVG. Import domToPng, or load the file from a script tag and call modernScreenshot.domToPng. It is a fork of html-to-image, which is itself a fork of dom-to-image.

Its own page: github.com. On this host: Where it sits against the rest.

what it adds to the install

186,023 bytes, 12 files, 0 deps. Version 4.7.0, published 16 April 2026, with an empty dependencies object. It is the smallest package on this grid and the only one under 200,000 bytes other than Tearline.

licence, and what it costs

MIT. The registry carries license MIT on version 4.7.0.

Free, nothing is sold. The repository page carries the licence as MIT license. There is no hosted tier and no account.

what it does not do

CSS counters. Its README's own TODO section names one thing it is unable to clone: css counters. A list numbered by content: counter(step) comes out of the export without its numbers. Nothing else is listed as unsupported on the page read, which is not a claim that nothing else is.

Column

SnapDOM. One function, plus plugins.

pick it instead of this when

You want one capture read out several ways, or you want more than an image from it. Calling it once returns a result you can then read as a PNG, a canvas, a Blob or a download, and that result keeps the state it captured even if the element changes afterwards. Its plugin packages reach past images entirely: self-contained HTML, page context for an agent, an image based PDF, an animated GIF and a recorded video.

what it produces

SVG, PNG, JPG, WebP, canvas, Blob. Its README: a browser capture engine for web interfaces, which captures rendered DOM state as a reusable result with styles, fonts and images included. Everything runs in the page using standard Web APIs, with no core dependencies. The GIF and video plugins record the live element over time rather than reusing the capture.

what you write

One function, plus plugins. await snapdom.toPng(card) for one output, or await snapdom(card) once and read the result several ways. The plugins are a second install, npm i @zumer/snapdom@latest @zumer/snapdom-plugins@latest, and the README requires the two majors to match.

Its own page: github.com. On this host: Where it sits against the rest.

what it adds to the install

565,573 bytes, 6 files, 0 deps. Version 3.1.1, published 24 September 2026, the most recently released package on this grid. The plugin package is a separate install and is not counted in that figure.

licence, and what it costs

MIT. The registry carries license MIT on version 3.1.1 of @zumer/snapdom.

Free, nothing is sold. The repository page carries the licence as MIT license. The documentation site and the plugin package are free as well, and there is no account.

what it does not do

No CommonJS build. Its README: there is no CommonJS build. It ships an ES module and a script tag build only, so a require call has nothing to load. The checkout documents version 3, and the version 2 source and documentation remain available separately for anything still on it.

Column

The browser on its own. Four calls you write yourself.

pick it instead of this when

What you are exporting is yours, and it has no remote fonts or images in it. Serialise the node, wrap it in an SVG foreignObject, decode that SVG in an img, draw it to a canvas, call toBlob. That is four browser calls and no package, and every browser side entry on this grid except html2canvas is a wrapper around those four calls with resource inlining and option handling added. The inlining is the part you take on yourself.

what it produces

A Blob or a data URL. MDN on toBlob: it creates a Blob object representing the image contained in the canvas. The type defaults to image/png, and a quality argument applies to the lossy formats. Nothing decides the shape of the output except the code you wrote.

what you write

Four calls you write yourself. MDN on the element: the foreignObject SVG element includes elements from a different XML namespace, which in the context of a browser is most likely HTML. That one line is what the whole technique rests on, and it is why every fork above produces the same kind of picture.

Its own page: developer.mozilla.org. On this host: The technique, written out.

what it adds to the install

Nothing, it is in the browser. MDN records the element as available across browsers since July 2015. There is no package to add, no version to pin, and nothing to bundle. What it costs you is code rather than bytes.

licence, and what it costs

No package, no licence. It is a specified element of SVG, documented by MDN, implementing the SVGForeignObjectElement interface. Nothing is licensed to you because nothing is distributed to you, which is the one row on this grid where a legal review has nothing to read.

Free, ships with the browser. Same reason as the licence: there is no package and no service. The real cost is the code you write to inline fonts, images and stylesheets before serialising, and that code is most of what the packages on this grid are.

what it does not do

You inline every asset yourself. MDN: as soon as you draw into a canvas any data that was loaded from another origin without CORS approval, the canvas becomes tainted, and reading it back throws a SecurityError. Inside the SVG data URI there is no origin at all, so a remote image, a webfont or an external stylesheet is never fetched and the export comes out blank or half drawn with nothing thrown.

Column

Tearline. The column that publishes this grid.

where it is the wrong answer

Most of the time. Six of the seven columns here take an element you already have and hand you a picture of it. This one does not do that at all. If the requirement is a PNG of this div, pick from the six and stop reading.

where it is the right one

When the receipt itself is the point. A wrapped listening history, an order summary, a share card made of rules and monospace. Then the look and the export arrive together rather than a rasteriser being bolted onto a div you styled yourself, and the receipt stays real text in the light DOM, so it is selectable, searchable and readable by a screen reader until the moment somebody asks for the PNG.

The attribute and method reference is in the documentation, the component is served unminified at /tearline.js, and the CSS behind the paper is a separate write-up.

What it produces: A receipt, then a PNG of it. The served file's own header names the four steps it runs: flatten the shadow tree and inline the slotted light DOM, serialise it with XMLSerializer into an SVG foreignObject, decode that SVG in an img, encode the canvas to a PNG blob. Four of the other columns here take the same route. What differs is that the receipt is already on the page as real text before any export happens.

What you write: A custom element you wrap. Its landing states the terms: wrap any HTML in one tag and it prints out as a thermal receipt. You write a tear-line element around markup you already have. Nothing is pointed at an element after the fact, and the paper is CSS around your content rather than a picture of it, so the text stays selectable.

What it adds to the install: 27,017 bytes, 4 files, 0 deps. The registry reports unpackedSize 27,017 across 4 files for version 0.2.0, published 14 September 2026, with an empty dependencies object. The file this host serves at /tearline.js is 22,766 bytes of unminified source, so you can read it before you load it.

Licence: MIT. The registry carries license MIT on version 0.2.0 of @compoundlabs/tearline. The bare name tearline is unregisterable, npm rejects it as too close to readline, which is why the package is scoped.

What it costs: Free, nothing is sold. Its pricing page says it plainly: Tearline is free and MIT-licensed, and there is nothing to buy. There is no account, no tier, and no API route on the host that could take a payment.

What it does not do: Renders a receipt, nothing else. Its own landing lists what it is not: not a template language, not an image service, not an ESC/POS driver, and it does not talk to a physical thermal printer. It also cannot be aimed at an element it did not wrap, which is the thing every other column on this grid does. Any img inside the receipt has to be a data URI or the export drops it.

Questions

Six questions. Answered from the grid.

What is the difference between this page and the html2canvas alternatives page?

That page ranks, this page registers. It answers which package to pick for a given job, in order, with a recommendation per case. This grid produces no order at all: every column answers the same six criteria and you decide which criterion decides it. If you already know what you need, read the ranking. If you do not yet know what the options are, read this.

Do I need a library to turn a DOM element into a PNG?

No. The browser has had SVG foreignObject since July 2015, per MDN read on 2026-09-19, and the whole technique is four calls: serialise the node with XMLSerializer, wrap it in an SVG foreignObject, decode that SVG in an img, draw it to a canvas and call toBlob. What a library adds is the inlining step, because the SVG is loaded as a data URI and therefore has no origin, so a remote image, a webfont or an external stylesheet will not load. Doing that by hand is the work, and it is why five packages exist to do it for you.

Which of these is the smallest?

modern-screenshot, at 186,023 bytes across 12 files with no runtime dependencies, read off the registry on 2026-09-19. Tearline is smaller at 27,017 bytes across 4 files, but it is a receipt component rather than a general rasteriser, so the two are not answering the same question. The largest is html2canvas at 3,379,055 bytes across 391 files, and it is the only entry here with runtime dependencies, two of them.

Why do my exports come out blank?

Two separate causes, and they fail differently. The SVG is loaded as a data URI, which is a sandbox with no origin, so it cannot fetch a remote image, a webfont or an external stylesheet: whatever the picture needs has to be inside the string before it is serialised, and when it is not, the export comes out blank or half drawn with nothing thrown. The other cause throws: per MDN, drawing data loaded from another origin without CORS approval taints the canvas, and toBlob, toDataURL and captureStream then raise a SecurityError. Neither is fixed by changing library. Inline the asset, serve it same origin, or send CORS headers.

Are any of these paid?

None of the six packages is. All five libraries carry license MIT on the registry and none of them publishes a hosted tier, an account or a paid edition, read on 2026-09-19. The browser route has no package and therefore no licence at all. Tearline is MIT and free, and its pricing page exists to say that there is nothing to buy.

How current are these figures?

Every cell carries the day it was read, and a job re-reads them rather than a person promising to. scripts/verify-compare.mjs re-fetches every source cited on this grid, and every source cited by the comparison pages on this host, and fails when the literal string a figure came from is no longer on its page. It last ran on 2026-09-19 and every check passed. Where a project's page does not carry a figure, the cell says not stated on the page read and names the page, because an inferred cell is a claim about somebody else's code that nobody checked.

Sources

Every figure here. Fetched, not remembered.

ColumnSourceWhat it suppliedRead
Tearlinetearline.thecompound.tech/tearline.jsWhat it produces2026-09-19
Tearlinetearline.thecompound.tech/What you write, What it does not do2026-09-19
Tearlineregistry.npmjs.org/@compoundlabs%2Ftearline/latestWhat it adds to the install, Licence2026-09-19
Tearlinetearline.thecompound.tech/pricingWhat it costs2026-09-19
html2canvasgithub.com/niklasvh/html2canvasWhat it produces, What you write, What it costs, What it does not do2026-09-19
html2canvasregistry.npmjs.org/html2canvas/latestWhat it adds to the install, Licence2026-09-19
html-to-imagegithub.com/bubkoo/html-to-imageWhat it produces, What you write, What it costs, What it does not do2026-09-19
html-to-imageregistry.npmjs.org/html-to-image/latestWhat it adds to the install, Licence2026-09-19
dom-to-image-moregithub.com/IDisposable/dom-to-image-moreWhat it produces, What you write, What it costs, What it does not do2026-09-19
dom-to-image-moreregistry.npmjs.org/dom-to-image-more/latestWhat it adds to the install, Licence2026-09-19
modern-screenshotgithub.com/qq15725/modern-screenshotWhat it produces, What you write, What it costs, What it does not do2026-09-19
modern-screenshotregistry.npmjs.org/modern-screenshot/latestWhat it adds to the install, Licence2026-09-19
SnapDOMgithub.com/zumerlab/snapdomWhat it produces, What you write, What it costs, What it does not do2026-09-19
SnapDOMregistry.npmjs.org/@zumer%2Fsnapdom/latestWhat it adds to the install, Licence2026-09-19
The browser on its owndeveloper.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlobWhat it produces2026-09-19
The browser on its owndeveloper.mozilla.org/en-US/docs/Web/SVG/Reference/Element/foreignObjectWhat you write, What it adds to the install, Licence, What it costs2026-09-19
The browser on its owndeveloper.mozilla.org/en-US/docs/Web/HTML/How_to/CORS_enabled_imageWhat it does not do2026-09-19

what re-checks this

Sizes, versions and licences move, so every source above carries the day it was read rather than one date at the bottom of the page. A job re-fetches each of these URLs and fails when the literal string a figure came from is no longer on it: scripts/verify-compare.mjs in this repository. It covers the sources cited here and the sources cited by the other comparison pages on this host, and it checks the package figures those pages print against what the registry returns today.

It last ran on 2026-09-19 and every check passed. If a figure here has drifted since, the project's own page is the authority and this one is not.

how a cell is sourced

Package sizes, file counts, versions, publish dates and licences come from the npm registry, which is the record npm itself serves. What a project does, what it returns and what it says it cannot do come from that project's own repository page or its own documentation, never from a roundup and never from memory.

A remembered version number is a wrong version number, which is why each cell names the exact string that had to be on the page for the figure to stand.

WHEN THE ELEMENT CHANGES

Tearline is one custom element with six attributes, three methods and no dependencies, served as an MIT package. Attributes get added and the export stages change with them. Leave an address and Tearline writes when a release changes the API.