To convert HTML to an image in Node.js, render the markup in a headless browser, wait for its fonts, images, and client-side code to finish, then call the browser screenshot API. Puppeteer and Playwright provide the most control; node-html-to-image is a simpler Puppeteer-based wrapper. The examples below cover HTML strings, remote pages, full-page captures, single elements, PNG/JPEG/WebP output, binary buffers, reliability, security, and production troubleshooting.
Choose the rendering approach
HTML is not an image format. A browser must calculate CSS layout, execute JavaScript, load web fonts and decode images before pixels exist. In Node.js, that normally means launching Chromium (or another supported browser), loading the document, waiting for application readiness, and calling screenshot().
| Option | Browser coverage | Control level | Output and best fit |
|---|---|---|---|
| ScreenshotNeo | Hosted browser screenshot API | One HTTP request; 63 capture options | Clean PNG, JPEG, WebP or PDF without maintaining a browser; only clean shots are billed |
| Puppeteer | Chromium-focused workflow | Low-level browser and page APIs | File or binary screenshot; established Chromium tooling |
| Playwright | Chromium, Firefox and WebKit contexts | Browser, context, page and locator APIs | File or Buffer; useful for cross-browser rendering or an existing Playwright stack |
| node-html-to-image | Puppeteer-backed | High-level HTML-to-image wrapper | PNG or JPEG, binary or base64; convenient for template-driven services |
Use Puppeteer when you want direct Chromium control, Playwright when browser-engine coverage matters, and the wrapper when a small templating service does not need low-level browser management. If you would rather not operate a browser process, ScreenshotNeo is the first hosted option to try because it removes consent banners, popups and chat widgets before capture, bills only clean shots, and has a $5 paid plan.
Set up a Node.js project
Use a current supported Node.js release and keep the browser dependency versioned in your lockfile. Create a project and install the library that matches your implementation:
mkdir html-image && cd html-image
npm init -y
npm install puppeteer
# or: npm install playwright
# or: npm install node-html-to-image
Puppeteer downloads a compatible browser during installation in its normal configuration. In restricted build environments, make sure the runtime can launch the browser and that required system libraries are present. Pin both the npm dependency and browser revision in production so a browser update does not silently change line breaks or colors.
#1 Best Overall
- Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
- Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
- Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
- Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
- 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
Convert an HTML string with Puppeteer
Puppeteer’s screenshot API is page.screenshot(). This complete example renders an HTML string, waits for web fonts and images, and writes a PNG:
import puppeteer from 'puppeteer';
const html = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
* { box-sizing: border-box; }
body { margin: 0; font-family: Arial, sans-serif; background: #f4f6f8; }
.card { width: 1200px; min-height: 630px; padding: 72px;
display: grid; align-content: center; background: white; }
h1 { margin: 0 0 20px; font-size: 64px; }
p { margin: 0; font-size: 28px; color: #475569; }
</style>
</head>
<body>
<main class="card">
<h1>Hello from Node.js</h1>
<p>Rendered HTML captured as a PNG.</p>
</main>
</body>
</html>`;
const browser = await puppeteer.launch({headless: true});
try {
const page = await browser.newPage();
await page.setViewport({width: 1200, height: 630, deviceScaleFactor: 1});
await page.setContent(html, {waitUntil: 'load'});
await page.evaluate(async () => {
if (document.fonts) await document.fonts.ready;
await Promise.all(Array.from(document.images).map(image =>
image.complete ? Promise.resolve() : new Promise(resolve => {
image.addEventListener('load', resolve, {once: true});
image.addEventListener('error', resolve, {once: true});
})
));
});
await page.screenshot({path: 'output.png', type: 'png'});
} finally {
await browser.close();
}
page.setContent() loads the supplied document. waitUntil: 'load' waits for the load event, but it does not guarantee that a client-rendered chart, a late image, or a web font is ready; the explicit readiness check handles those common cases. The viewport and device scale factor determine the pixel dimensions. A 1200 by 630 viewport at scale 1 produces a 1200 by 630 CSS-pixel capture for this fixed-size card.
Capture a remote page
For a URL, use page.goto() instead of setContent():
PC 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 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteawait page.goto('https://example.com', {waitUntil: 'networkidle2'});
await page.waitForSelector('main');
await page.screenshot({path: 'remote.png', fullPage: true, type: 'png'});
networkidle2 is only a signal that network activity has become quiet. Analytics, polling, advertisements, service workers, and lazy content can keep a page busy or can load after the signal. Prefer an application-specific selector or readiness flag when you control the page. For a page that exposes window.__SCREENSHOT_READY__, wait with page.waitForFunction(() => window.__SCREENSHOT_READY__ === true).
Rank #2
- 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
- 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
- Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
- 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
- What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
Use Playwright instead
Playwright has a similar API and returns a Buffer when no path is supplied:
import { chromium } from 'playwright';
const browser = await chromium.launch();
try {
const page = await browser.newPage({viewport: {width: 1200, height: 630}});
await page.setContent('<main><h1>Hello</h1></main>');
await page.evaluate(() => document.fonts?.ready);
const buffer = await page.screenshot({type: 'png'});
console.log(`Generated ${buffer.length} bytes`);
// Send buffer to object storage or an HTTP response here.
} finally {
await browser.close();
}
Playwright supports Chromium, Firefox and WebKit contexts. Keep generation and visual comparison in the same browser, operating system, font set and locale: screenshots can differ between platforms because browser rendering, fonts and related environment details differ.
Choose full-page, element, format and scale settings
Full document versus viewport
Use fullPage: true when the image should include the entire scrollable document. A normal screenshot captures only the current viewport. Very long pages create large bitmaps; an element capture or an explicit clip is usually safer for memory and file size.
await page.screenshot({path: 'article.png', fullPage: true, type: 'png'});
Capture one component
When you need a card, invoice, chart or component, capture its locator rather than the whole page:
Rank #3
- Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
- Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
- Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
- Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
- What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.
await page.locator('.invoice').screenshot({path: 'invoice.png', type: 'png'});
Wait for the locator to be visible and stable before capturing. If the element is outside the viewport, Playwright scrolls it into view; Puppeteer can use page.$eval() with an element handle and its screenshot method.
PNG, JPEG and WebP
- PNG: lossless and suitable for text, diagrams and transparency.
- JPEG: generally smaller for photographic content; set a quality value where the selected API supports it.
- WebP: compact modern output where the browser API supports it.
await page.screenshot({path: 'photo.jpg', type: 'jpeg', quality: 82});
await page.screenshot({path: 'hero.webp', type: 'webp', quality: 85});
Use deviceScaleFactor (Puppeteer) or an equivalent scale setting when you need high-density output. A higher scale increases pixel dimensions, memory use and encoding time. PNG supports transparent backgrounds when the page and screenshot settings leave the background transparent; JPEG cannot represent transparency.
Return bytes instead of writing a file
Omit path to receive binary data. Puppeteer returns a Uint8Array and Playwright returns a Buffer, which can be uploaded directly:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →const bytes = await page.screenshot({type: 'png'});
await fs.promises.writeFile('output.png', bytes);
Use node-html-to-image for templates
node-html-to-image wraps Puppeteer and is useful when the input is a template with values. It supports selector targeting, transparent PNG output, binary or base64 encoding, wait settings, custom Puppeteer injection and maximum concurrency.
Rank #4
- Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
- Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
- Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
- Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
- Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
import nodeHtmlToImage from 'node-html-to-image';
const image = await nodeHtmlToImage({
html: '<html><body><h1>{{title}}</h1></body></html>',
content: {title: 'Invoice'},
type: 'png',
selector: 'body',
transparent: true
});
await import('node:fs/promises').then(fs => fs.writeFile('invoice.png', image));
This convenience layer reduces boilerplate, but direct Puppeteer or Playwright is preferable when you need browser contexts, request interception, authentication flows, detailed readiness logic or cross-browser coverage.
Make output reproducible and safe
Control layout inputs
- Set an explicit viewport, device scale factor, locale and timezone.
- Install and pin the fonts used by the design; fallback fonts change line breaks and dimensions.
- Disable or freeze CSS animations, transitions, carousels and timestamps when deterministic output matters.
- Wait for
document.fonts.ready, image decoding and your application’s own ready signal. - Use a fixed color scheme or explicitly set dark mode rather than inheriting an unpredictable preference.
Protect the renderer
Untrusted HTML is executable browser input. Restrict scripts and external requests when rendering user content, validate URLs, limit navigation to allowed hosts, and avoid exposing cloud credentials or internal services to page JavaScript. Resource interception can block trackers, advertisements or unexpected origins, but test it carefully because blocking a stylesheet or font can change the result.
Improve throughput
Launching a browser for every image is slow and expensive. Start one browser process, create a fresh page or context per job, and close each page in a finally block. Bound concurrency so many large full-page images do not exhaust memory. For batches, prefer element captures or controlled clips and reuse the process while recycling pages that accumulate state.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Common failures and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Blank or partially rendered image | Capture ran before client rendering, fonts or images completed | Wait for a selector, a readiness flag, document.fonts.ready, image decoding or a deliberate delay; inspect console errors. |
| Wrong dimensions | Implicit viewport, device scale or responsive breakpoint | Set viewport width, height and scale explicitly; include the correct mobile or desktop meta viewport. |
| Missing fonts or changed line breaks | Font request failed or a fallback font was used | Install the font in the runtime, wait for the font promise, and verify network responses. |
| Lazy images are absent | Images load only after scrolling or intersection events | Use full-page behavior, scroll through the document before capture, or expose a page-ready signal after lazy loading. |
| Navigation timeout | Long polling, slow third-party resources or a page that never becomes idle | Use a realistic timeout, wait for a specific selector instead of network idle, and block nonessential requests. |
| Browser fails to launch in a container | Missing shared libraries, sandbox restrictions or an unavailable browser binary | Install the runtime dependencies, use the documented container setup, and only adjust sandbox flags when the deployment security model permits it. |
| Different pixels in CI and locally | Different browser revision, OS, fonts, locale or animation state | Pin versions and fonts, use the same image, locale and timezone settings, and freeze animations. |
| Out-of-memory termination | Huge full-page bitmap, high scale or excessive parallel jobs | Capture an element or clip, reduce scale, limit concurrency and reuse a controlled browser process. |
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. It accepts one GET request and returns PNG, JPEG, WebP or PDF. Before capture it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and response headers identify the page verdict and whether the request was billed.
Its 63 options cover full-page captures with lazy images loaded, CSS-selector element captures, dark mode, 12 device presets or any viewport, retina scale, PDF paper size and margins, HTML/CSS to image, custom JavaScript and CSS, pre-capture clicks, hidden selectors, waits for selectors, delays or network idle, blocking ads, trackers, requests or resource types, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, image resizing, configurable-TTL caching, signed links for public <img> tags, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which helps when migrating.
Best Value
- 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
- Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
- Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
- HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
- What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
Use the ScreenshotNeo documentation for the complete parameter list. The same endpoint can be called from any Node.js service:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const image = Buffer.from(await res.arrayBuffer());
Equivalent cURL and Python calls are useful for testing or non-Node workers:
Free tools Windows power users keep installed
One-click scans. No signup required.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. Every feature is available on every plan:
| Plan | Included shots per month | Price |
|---|---|---|
| Free | 1,000 | $0, no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
Yearly billing gives two months free. If you want clean captures without installing or maintaining Chromium, sign up for the free ScreenshotNeo plan: it includes 1,000 screenshots a month with no card.
Frequently Asked Questions
Can I send a screenshot directly from an Express route?
Yes. Omit the local file path, set the response content type to the selected format (for example, image/png), and write the returned Buffer or Uint8Array to the response. Handle browser or navigation errors before sending headers.
How should I render a page that requires login?
Create an authenticated browser context, set cookies or an Authorization header before navigation, and restrict the destination host. Never place credentials in the HTML itself or expose them to untrusted page scripts.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Why does a screenshot pass locally but fail in continuous integration?
The rendering environment is different. Align the browser revision, operating system libraries, installed fonts, locale, timezone, viewport, scale and animation state, then compare captures generated in that same controlled environment.
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.

