To convert HTML to JPG in Node.js, render the markup in a browser and save a JPEG screenshot. Playwright can capture either the visible viewport or the full scrollable page, and its screenshot API accepts a JPEG quality setting. The key decision is whether your HTML is already hosted at a URL or needs to be loaded directly into a page.
Choose a browser-based method
HTML is a description of a page, not an image. A browser must interpret its markup, CSS, fonts, images, and JavaScript before there are pixels to save. A screenshot therefore converts the rendered page—not the original HTML source—into a JPG.
For a Node.js project, Playwright offers a direct screenshot API with JPEG output, a quality option, and viewport or full-page capture. Puppeteer is another browser-automation option; its screenshot API can return image data as bytes or a base64 string, which can suit applications that need to process or transmit the image in memory. Choose according to the controls and output form your project needs rather than assuming one library is universally better.
The examples below use Playwright. Check the API documentation for the Playwright version installed in your project before relying on exact option behavior; the available settings can vary by version.
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
Install Playwright and prepare Node.js
In an existing Node.js project, install Playwright and its browser binaries using the commands appropriate to your package manager and deployment environment. A typical npm setup is:
npm install playwright
npx playwright install chromium
If your deployment environment does not allow downloading browsers during installation, install the required browser in the build or runtime image instead. The Node.js package alone is not necessarily enough: screenshot capture needs a compatible browser executable. Use the same browser installation and version in development and production when consistent output matters.
Convert a URL to a JPG
This complete example opens a URL, waits for the document to load, captures a full-page JPEG, and closes the browser even if navigation or capture fails. Save it as capture-url.mjs and run it with node capture-url.mjs.
import { chromium } from 'playwright';
const targetUrl = process.argv[2] ?? 'https://example.com';
const outputPath = process.argv[3] ?? 'output.jpg';
let browser;
try {
browser = await chromium.launch({ headless: true });
const page = await browser.newPage({
viewport: { width: 1280, height: 800 },
deviceScaleFactor: 1,
});
await page.goto(targetUrl, { waitUntil: 'load', timeout: 60_000 });
await page.screenshot({
path: outputPath,
type: 'jpeg',
quality: 80,
fullPage: true,
});
console.log(`Saved ${outputPath}`);
} catch (error) {
console.error('Could not capture the page:', error);
process.exitCode = 1;
} finally {
if (browser) await browser.close();
}
Pass a different URL and destination as command-line arguments, for example node capture-url.mjs https://example.com page.jpg. Replace the example address with a page you are authorized to capture. The load event indicates that the page’s load event has fired; it does not guarantee that every application-specific animation, delayed API request, or lazy-loaded image is finished. If the page has its own rendering signal, wait for that signal before taking the screenshot.
Convert an HTML string to a JPG
For markup generated by your application, use page.setContent() to place it in a browser page before capturing. This example reads an HTML file, waits for fonts to be ready, then writes a full-page JPEG.
import { readFile } from 'node:fs/promises';
import { chromium } from 'playwright';
const html = await readFile('page.html', 'utf8');
let browser;
try {
browser = await chromium.launch({ headless: true });
const page = await browser.newPage({
viewport: { width: 1200, height: 900 },
deviceScaleFactor: 1,
});
await page.setContent(html, { waitUntil: 'load', timeout: 60_000 });
await page.evaluate(() => document.fonts.ready);
await page.screenshot({
path: 'page.jpg',
type: 'jpeg',
quality: 85,
fullPage: true,
});
console.log('Saved page.jpg');
} catch (error) {
console.error('Could not convert HTML:', error);
process.exitCode = 1;
} finally {
if (browser) await browser.close();
}
Relative URLs in the HTML need a base URL to resolve correctly. For example, <img src="images/logo.png"> may not point to the file you expect when the page has no meaningful origin. Use absolute asset URLs, inline assets where appropriate, or set a base element whose URL matches your assets. Also make sure the browser process can reach external images, stylesheets, and fonts.
Rank #2
Pick capture dimensions and scope
Viewport screenshot
Omit fullPage or set it to false to capture the visible viewport. The screenshot dimensions follow the page viewport and device scale configuration. This is the right choice when the consumer expects a fixed-size image, such as a card preview or a screenshot of the currently visible interface.
Full-page screenshot
Set fullPage: true to include the scrollable document. This can produce a very tall image, especially for long pages. Confirm that the system receiving the JPG accepts those dimensions and file size. A full-page capture is not a substitute for a multi-page PDF when the intended output is a paginated document.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Viewport size and scale
Set the viewport before navigation or content rendering when layout depends on screen width. A narrow viewport can trigger mobile breakpoints; a wider viewport can expose desktop layouts. deviceScaleFactor affects pixel density, so check the actual output dimensions in your environment if a consuming system requires a specific width or height. Do not infer final image dimensions solely from CSS pixel values.
Set JPEG quality and handle image data
The screenshot option type: 'jpeg' requests JPEG output, and quality controls JPEG compression quality. JPEG is lossy: it can be a practical choice for photographic or web-preview content, but fine text, thin lines, and sharp color boundaries may show compression artifacts. Try a few values against your own page and inspect the result; there is no universally correct quality setting. PNG does not use the JPEG quality option.
JPEG does not support transparency. If transparent output is required, use a format that supports it, such as PNG, rather than expecting a JPEG screenshot to preserve a transparent background.
Playwright can save a screenshot to a path and also returns image data from its screenshot call. If you need the bytes for an upload or another in-memory operation, omit path and retain the returned value:
Rank #3
const imageBytes = await page.screenshot({
type: 'jpeg',
quality: 80,
fullPage: true,
});
// Example: write the returned bytes to a file when needed.
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('output.jpg', imageBytes)
);
With Puppeteer, the screenshot API documents byte-array and base64 output forms as well. Consult the documentation for the installed package to choose the appropriate form and configure its JPEG options.
Wait for the page to be ready
There is no single wait condition that guarantees every page is visually complete. Static pages may be ready at the load event, while applications may render content after API calls or use animations and lazy loading. Decide what “ready” means for the page you are capturing and wait for a page-specific condition where possible.
- Known content: wait for a selector that appears when the required content is rendered.
- Fonts: wait for
document.fonts.readyif the final typography matters. - Delayed effects: use a bounded delay only when the page has a known delay that cannot be detected with a selector or application signal.
- Lazy content: ensure the relevant content has been brought into view or otherwise loaded before capturing; merely waiting for the initial load event may not load everything below the fold.
Keep waits bounded with timeouts and surface failures to the caller. Waiting indefinitely can tie up browser processes and make a batch job appear stuck.
Browser and library choices
| Need | Practical fit | Trade-off |
|---|---|---|
| JPEG path, quality, viewport or full-page control | Playwright screenshot API | Run and maintain a browser compatible with the installed package; verify exact options for your version. |
| Image data in bytes or base64 | Puppeteer screenshot API | Confirm its output and option details against the version installed in your project. |
| Repeatable visual output | Either library, with a controlled browser environment | Rendering may vary with operating system, browser version, settings, hardware, power source, and headless mode. |
This is a focused comparison of the screenshot controls and output forms relevant to HTML-to-JPG conversion, not a comprehensive ranking of the two libraries.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Or skip the browser setup
If you need a screenshot from a URL without installing and operating a browser in your own Node.js environment, ScreenshotNeo provides a website screenshot API and MCP server. Its API returns an image or PDF from a GET request. The example below uses the Node.js built-in fetch API; store your API key in an environment variable in real applications rather than placing it in source code.
const q = new URLSearchParams({
access_key: process.env.SCREENSHOTNEO_API_KEY,
url: 'https://stripe.com',
format: 'jpg',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('shot.jpg', Buffer.from(await res.arrayBuffer()))
);
See the ScreenshotNeo API documentation for request parameters and response details. In addition to the URL-based Node.js example above, the API can be called with cURL or Python:
Rank #4
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)
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; individual steps can be turned off.
- Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses identify the page verdict and billing status in headers.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents and MCP clients. - The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.
Sign up for ScreenshotNeo to start with 1,000 free screenshots a month, no card required.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance, reliability, and cost considerations
A local browser capture requires browser startup, page rendering, and image encoding. The time and memory depend on the page and the execution environment; no universal performance figure applies to every site. Reuse browser processes where appropriate for repeated work, but create and close pages deliberately and close the browser during cleanup. For large full-page captures, monitor memory and output dimensions, and consider whether a viewport capture better fits the actual use.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesFor repeatable results, keep the operating system, browser version, launch settings, viewport, device scale, fonts, and relevant resources consistent. A screenshot may differ across host operating systems, browser versions, hardware, power sources, and headless settings even when the HTML is unchanged. Do not use visual pixel equality as an assumption unless those conditions are controlled.
Local browser automation has no per-shot API price, but it does require compute resources and operational care. A hosted screenshot API instead charges according to its plan and billing rules. For ScreenshotNeo’s published options, monthly plans are Free (1,000 shots), Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000); yearly billing gives two months free. All features are included on every plan. Choose based on actual volume and operating requirements rather than an assumed cost per screenshot for your own browser setup.
Troubleshooting common conversion failures
The browser will not launch
Check that the Playwright package and the required browser binary are both installed for the runtime environment. A browser installed on a developer’s machine may not exist inside a production container. Install the browser as part of the deployment image and verify its compatibility with the installed package.
The JPG is blank or missing page content
Check whether navigation completed successfully and whether the page renders content asynchronously. Wait for a meaningful selector or application-ready condition rather than relying on an arbitrary short delay. For setContent(), check for failed relative asset URLs and ensure external resources are reachable.
Fonts or images look different
Make sure the page has loaded the intended font and image resources before capture. Wait for the relevant content, and, when typography is important, wait for fonts to be ready. Compare the rendering environment with the one used for expected output; missing fonts or environment changes can alter layout.
The output is unexpectedly cropped
A standard screenshot captures the viewport, not necessarily the entire document. Set fullPage: true when you need the scrollable page. If the output is too tall, inspect the page’s actual document height and consider capturing a viewport or a particular region instead.
The file looks blurry or has artifacts
JPEG compression is lossy. Increase the quality value and compare output, or choose PNG if preserving sharp edges matters more than file size. Also check viewport dimensions and device scale: an image rendered at a smaller pixel size and enlarged later can look soft.
The page hangs or takes too long
Use a finite navigation timeout and a specific readiness condition. Some pages keep network connections open or perform continual background activity, so waiting for a universal “everything is done” state can be unreliable. Report the timeout to the caller and close the browser in a finally block to avoid leaving processes behind.
Frequently Asked Questions
Does converting HTML to JPG preserve clickable links or page behavior?
No. A JPG is a raster image of the rendered page; links and interactive behavior are not carried into the image.
Can I create a JPG from HTML without a browser?
The method covered here relies on browser rendering so that CSS, fonts, images, and JavaScript can affect the resulting pixels.
Can I use these captures for commercial pages?
Check the site’s access rules, your organization’s policies, and any applicable rights before capturing or redistributing a page.
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.
Recommended Free Tools




