The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Direct answer: choose the rendering location before choosing a package. For an element that already exists in a browser, html-to-image can export the DOM node to PNG, JPEG, SVG, Blob, canvas, or pixel data. For server-side HTML templates, node-html-to-image runs Puppeteer in headless Chromium. For navigation, full-page capture, and browser automation, use Playwright or Puppeteer directly. If you do not want to operate a browser runtime, ScreenshotNeo provides a one-request screenshot API and an MCP server for AI agents.
Choose the rendering model first
These approaches solve different problems. A browser-side library starts with a DOM node that your application has already rendered. A Node.js package or browser automation framework creates a browser page, inserts HTML or navigates to a URL, waits for the desired state, and captures it. An external screenshot API performs that browser work for you.
| Use case | Best starting point | What is captured | Main consideration |
|---|---|---|---|
| Export one element in an existing web app | html-to-image |
A DOM node and its subtree | Fonts, images, cross-origin content, and data-URI limits |
| Render supplied HTML on a Node.js server | node-html-to-image |
Template output or a selected element | Chromium/Puppeteer deployment and deterministic waiting |
| Navigate to pages or automate a browser | Playwright or Puppeteer | Viewport, full page, or selected element | Viewport, device scale, readiness, and browser lifecycle |
| Generate screenshots without managing Chromium | ScreenshotNeo | URL, HTML, element, or PDF according to request options | API key and request configuration |
Documentation describes APIs rather than a controlled speed or fidelity benchmark, so test your own HTML, asset mix, and deployment before making a performance claim.
Browser-side conversion with html-to-image
html-to-image clones the selected subtree, copies computed styles, reconstructs pseudo-elements, embeds fonts and images, serializes the clone as SVG using foreignObject, and can rasterize that SVG through an off-screen canvas. Its promise-based functions include toPng, toJpeg, toBlob, toSvg, toCanvas, and toPixelData.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problems#1 Best Overall
Install and export a PNG
npm install html-to-image
import { toPng } from 'html-to-image';
const node = document.querySelector('#invoice');
if (!(node instanceof HTMLElement)) {
throw new Error('Expected #invoice to be an HTMLElement');
}
const dataUrl = await toPng(node, {
cacheBust: true,
pixelRatio: 2
});
const link = document.createElement('a');
link.download = 'invoice.png';
link.href = dataUrl;
link.click();
pixelRatio increases output pixels relative to CSS pixels. Confirm the target browser and output dimensions before using a high value, because a large subtree can exceed browser memory or data-URL limits.
Other output types
import { toJpeg, toBlob, toSvg, toCanvas, toPixelData } from 'html-to-image';
const node = document.querySelector('#card') as HTMLElement;
const jpegUrl = await toJpeg(node, { quality: 0.92 });
const blob = await toBlob(node);
const svgUrl = await toSvg(node);
const canvas = await toCanvas(node);
const pixels = await toPixelData(node);
// Example: upload the Blob
if (blob) {
await fetch('/uploads/card', { method: 'POST', body: blob });
}
Make browser-side output reliable
- Wait until web fonts have loaded:
await document.fonts.ready. - Wait for images and ensure they have usable dimensions before calling the library.
- Use same-origin assets or configure image servers for cross-origin use. A canvas tainted by cross-origin content cannot be read successfully.
- Keep the exported subtree manageable. The project warns that large DOMs can fail because data-URI limits vary by browser.
- Use a current Chrome, Firefox, or Safari; the project documentation says Internet Explorer is not supported.
The project documentation reports that Chrome performed significantly better for large DOM trees in its tested context. Treat that as a project-specific qualitative observation, not a universal benchmark.
Server-side HTML with node-html-to-image
node-html-to-image uses Puppeteer in headless mode and documents TypeScript support. It can render a template to PNG or JPEG, write a file, return binary or base64 data, target a selector, and run hooks before setting HTML or before taking the screenshot.
Install and render a TypeScript template
npm install node-html-to-image
npm install -D typescript tsx @types/node
import nodeHtmlToImage from 'node-html-to-image';
const result = await nodeHtmlToImage({
output: './dist/card.png',
html: `
<html>
<head>
<style>
* { box-sizing: border-box; }
body { margin: 0; width: 1200px; font-family: Arial, sans-serif; }
.card { width: 1200px; padding: 64px; background: #101827; color: white; }
h1 { margin: 0 0 16px; font-size: 54px; }
</style>
</head>
<body>
<section class="card">
<h1>{{title}}</h1>
<p>{{subtitle}}</p>
</section>
</body>
</html>`,
content: {
title: 'TypeScript rendering',
subtitle: 'Generated on the server'
},
selector: '.card',
type: 'png',
waitUntil: 'networkidle0'
});
console.log(result);
Set dimensions in CSS, commonly on body or the selected element. Use a pre-screenshot hook when application data, fonts, or images need to be prepared. A deterministic job should define the viewport, wait condition, and timeout rather than relying on an immediate capture.
Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
When this route is a good fit
- Use it for invoices, social cards, reports, and other HTML templates generated by a Node.js service.
- Plan for the Chromium runtime in local, container, and serverless deployments.
- Make external fonts and images reachable from the rendering environment, or embed them.
- Reuse a browser process for batches when appropriate, while isolating pages and closing resources on shutdown.
Direct browser automation with Playwright
Playwright is appropriate when you need navigation, interactions, custom viewports, or full-page capture. Its Page API supports a TypeScript-compatible screenshot flow, output paths, image quality, and CSS-pixel or device-pixel scaling.
npm install playwright
npx playwright install chromium
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2
});
try {
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.evaluate(() => document.fonts.ready);
await page.screenshot({
path: 'page.png',
fullPage: true,
type: 'png'
});
} finally {
await browser.close();
}
Use a selector when only one component is needed:
const chart = page.locator('#chart');
await chart.screenshot({ path: 'chart.png' });
Playwright’s scale setting determines whether output follows CSS pixels or device pixels. Choose explicitly so a retina screenshot is not confused with a larger layout viewport. Wait for an application-specific selector when network idle is not sufficient.
Direct browser automation with Puppeteer
Puppeteer’s Page.screenshot() returns a base64 string or Uint8Array, depending on the overload. It is useful when your project already uses Puppeteer or needs its browser and page APIs.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setViewport({ width: 1200, height: 800, deviceScaleFactor: 2 });
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
await page.evaluate(() => document.fonts.ready);
const bytes = await page.screenshot({ type: 'png', fullPage: true });
await Bun.write('page.png', bytes);
} finally {
await browser.close();
}
In a Node.js project that does not provide Bun.write, write the returned bytes with your runtime’s filesystem API, for example writeFile from Node’s fs/promises.
Rendering checklist for consistent images
- Fix the viewport: set width, height, and device scale before loading content.
- Define readiness: wait for a selector, a known application state, fonts, and images.
- Control dimensions: set CSS width and height for cards; use full-page capture only when the entire document is intended.
- Control assets: verify image URLs, CORS headers, authentication, and font loading from the rendering environment.
- Choose format: PNG preserves sharp text and transparency; JPEG is smaller for photographic content but has quality loss; SVG is useful when the consumer accepts vector output.
- Clean up: close pages and browsers, and limit concurrency so rendering jobs do not exhaust memory.
Common failures and fixes
Blank or partially rendered output
The capture ran before data, fonts, or images finished. Add a selector-based readiness condition, wait for document.fonts.ready, and check image completion in the page before taking the screenshot.
Images or fonts are missing
The browser cannot reach the asset, the URL requires authentication, or cross-origin policy blocks it. Serve assets to the rendering environment, provide the required credentials, embed critical assets, and inspect network failures.
“Canvas is tainted” or export throws a security error
This usually indicates cross-origin content. Configure the image server for cross-origin use and load it with the appropriate CORS behavior, or move the asset to the same origin. Browser-side exports remain subject to canvas security rules.
Large DOM fails or produces an oversized data URL
Reduce the subtree, lower the pixel ratio, split a long document into sections, or use a headless-browser screenshot that writes binary output instead of a data URL. Browser data-URI limits differ.
Chromium cannot launch in production
Install the browser required by Playwright or Puppeteer, include system dependencies in the container, and verify executable permissions. Keep the browser version aligned with the package and test the exact deployment image.
Output dimensions are surprising
CSS pixels, device pixels, body dimensions, and full-page layout are separate concepts. Log the viewport and element bounding box, then set the intended width, height, and scale explicitly.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo accepts one GET request and returns a PNG, JPEG, WebP, or PDF. It can capture a URL or supplied HTML and includes options for full-page output with lazy images, CSS-selector element capture, dark mode, 12 device presets or any viewport, retina scale, PDF paper and page ranges, custom CSS and JavaScript, pre-capture clicks, hidden selectors, selector/delay/network-idle waits, blocked ads or resource types, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of 100 URLs per call, usage reporting, and an OpenAPI specification. Parameters used by other screenshot APIs also work.
Before capture, it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and whether it was billed. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11See the ScreenshotNeo documentation for request options. A minimal call is:
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
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(`Screenshot failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to get started.
Cost, performance, and reliability decisions
Browser-side conversion avoids a server browser but consumes the user’s memory and is constrained by browser security and data-URL limits. Headless rendering centralizes output and supports navigation, but Chromium startup, memory, and concurrency become operational costs. Reuse a controlled browser process for batches, isolate pages, cap concurrent jobs, and record failures with the URL, viewport, wait condition, and browser version. ScreenshotNeo shifts browser operations to an API: choose its cache TTL for repeat captures, use asynchronous jobs for long work, and use bulk requests for up to 100 URLs.
FAQ
Can TypeScript itself render HTML?
TypeScript supplies types and compiles to JavaScript; a browser renderer, DOM-to-image library, or headless browser performs the actual rendering.
Should I use PNG or JPEG?
Use PNG for text, interfaces, and transparency. Use JPEG when photographic content and a smaller lossy file are more important.
Can I capture an HTML string without a URL?
Yes. Set page content in Playwright or Puppeteer, or pass a template to node-html-to-image. A browser-side library instead requires an existing DOM node.
Quick Recap
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.

