Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a headless browser to render the HTML, then call its screenshot API. Puppeteer and Playwright both support saving a PNG to a file or returning image bytes for an upload or image-processing pipeline. For a web page, navigate to its URL; for an HTML string, set the page content and wait for the assets your page needs before capture.

Convert a web page to PNG with Puppeteer

Puppeteer controls a browser that renders HTML, CSS, images, fonts, and client-side JavaScript. Install Puppeteer in your Node.js project, then navigate to the page and capture it. The following ES module writes a full-page PNG:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'output.png', fullPage: true });
} finally {
  await browser.close();
}

Install the package with npm install puppeteer. In a project configured for CommonJS rather than ES modules, use const puppeteer = require('puppeteer'); and place the asynchronous work inside an async function.

What the options do

  • launch() starts the browser. The try/finally pattern ensures it closes even if navigation or capture fails.
  • newPage() opens a page, and setViewport() fixes its viewport dimensions and pixel scale. A known viewport helps make repeated renders consistent.
  • goto() loads the target URL. networkidle2 waits for a period with no more than two network connections, but it cannot guarantee that application-specific data, lazy images, or animations have settled.
  • screenshot({ path: 'output.png' }) writes the image to disk. fullPage: true captures beyond the current viewport; omit it for a viewport-sized image.

Puppeteer documents Page.screenshot() as its page-capture method. PNG is the default screenshot type, so a type option is not required. JPEG quality settings do not apply to PNG.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Convert an HTML string instead of a URL

When the HTML is generated inside your Node.js process, use page.setContent() rather than navigating to a website. This runnable example writes a simple page as a PNG:

import puppeteer from 'puppeteer';

const html = `
  <!doctype html>
  <html>
    <head>
      <meta charset="utf-8">
      <style>
        body { font: 16px Arial, sans-serif; padding: 32px; }
        .card { width: 420px; padding: 24px; background: #f2f5f9; }
      </style>
    </head>
    <body>
      <div class="card"><h1>Rendered in Node.js</h1><p>HTML becomes pixels in a browser.</p></div>
    </body>
  </html>
`;

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 900, height: 700, deviceScaleFactor: 1 });
  await page.setContent(html, { waitUntil: 'networkidle0' });
  await page.screenshot({ path: 'card.png', fullPage: true });
} finally {
  await browser.close();
}

For external images, stylesheets, or web fonts, use URLs the browser environment can reach, or embed the assets in the HTML. If the page relies on client-side code to populate content, wait for a page-specific selector or other signal before taking the screenshot; network idle alone may occur before that work is complete.

Save the PNG to a file or return it as bytes

Both libraries let you choose how to consume the captured image. With Puppeteer, passing path writes a file; omitting it returns image data. Playwright documents its screenshot method as returning a buffer when no file path is supplied.

// Puppeteer: write a file
await page.screenshot({ path: 'output.png' });

// Puppeteer: keep the image data in memory
const imageBytes = await page.screenshot();

// Playwright: write a file
await page.screenshot({ path: 'output.png' });

// Playwright: get a Buffer
const imageBuffer = await page.screenshot();

Use bytes when you need to upload the PNG, pass it to an image library, or return it from an HTTP endpoint. Be mindful that a large full-page capture occupies memory; release references when processing is finished. Puppeteer’s and Playwright’s screenshot documentation describes these file and buffer behaviors in their respective APIs: Puppeteer Page.screenshot() and Playwright Page.screenshot().

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture a full page or a single element

Full-page output

For a long article or report, set fullPage: true to capture the scrollable page rather than only the visible viewport. In Puppeteer, this option belongs in page.screenshot(); Playwright supports the same full-page setting. Ensure lazy-loaded images have actually loaded before capture, since scrolling the page into view may be necessary to trigger them.

Element-only output

For a card, invoice, or chart, capture the element instead of the whole page. Puppeteer supports ElementHandle.screenshot(); Playwright supports screenshots through a locator. This avoids including unrelated page content, and the element’s dimensions determine the captured region.

// Puppeteer element capture
const element = await page.waitForSelector('.invoice');
if (!element) throw new Error('Invoice element was not found');
await element.screenshot({ path: 'invoice.png' });

In Playwright, the corresponding pattern is await page.locator('.invoice').screenshot({ path: 'invoice.png' });. Wait for the target element to exist and be visible before capturing it.

Use Playwright instead of Puppeteer

Playwright offers the same basic flow: launch a browser, create a page, navigate, and call page.screenshot(). Its official API documents the screenshot method and returned buffer.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
  await page.goto('https://example.com', { waitUntil: 'networkidle' });
  await page.screenshot({ path: 'output.png', fullPage: true });
} finally {
  await browser.close();
}

Install Playwright with npm install playwright. Browser binaries may need to be installed for the browser you select; follow the library’s installation instructions for your environment. Puppeteer and Playwright both need a usable browser engine at runtime. The cited APIs establish capture behavior, not a speed advantage for either library, so choose based on your existing dependencies, browser requirements, and deployment environment.

Playwright’s screenshot controls include full-page capture and locator screenshots. Puppeteer also supports transparent backgrounds with omitBackground and a selected screen region with clip; use those when those controls fit the output you need. See the respective documentation for exact options: Puppeteer screenshot API and Playwright screenshot API.

Make captures more reliable

A screenshot records the browser’s rendered state at capture time. A successful navigation does not prove that every visual dependency is ready, so decide what “ready” means for your page.

  • Wait for page-specific content. If JavaScript inserts a chart or report, wait for its selector or application-ready signal before capture.
  • Account for fonts and images. Confirm remote assets are reachable from the browser process. If an image is lazy-loaded, scroll it into view or otherwise trigger its load, then wait for completion.
  • Set a fixed viewport. Specify width, height, and device scale factor when output dimensions matter. Responsive layouts can look different at different viewport widths.
  • Control animation when needed. Await or disable transitions and animations if a mid-animation frame would make the result inconsistent.
  • Choose the output scope deliberately. Use viewport capture for what is currently visible, full-page capture for the scrollable document, and element capture for one component.
  • Close the browser. Use finally or equivalent cleanup so an error does not leave a browser process running.

These are implementation precautions, not guarantees of visual determinism: the page’s own scripts, external assets, and runtime conditions still affect what appears.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshoot common HTML-to-PNG problems

The screenshot is blank or missing content

Check that navigation succeeded and that the target selector exists before capture. A page can load its initial document and still be waiting for client-side data. Wait for the application’s ready state or a visible element rather than relying only on a generic network-idle condition.

Images or web fonts are missing

Verify that asset URLs are absolute or resolve correctly relative to the document, and that the browser process can access those hosts. For HTML strings, supply reachable URLs or embed the assets. Trigger lazy loading and wait for the required assets before calling screenshot().

The page is cut off

Viewport capture includes only the visible area. Set fullPage: true for a full-page image, or capture the specific element. If the result is unexpectedly short, check whether content is added only after scrolling or whether the page uses a separate scrolling container.

Fonts, layout, or colors differ between runs

Set a known viewport and device scale factor, ensure fonts and stylesheets have loaded, and wait for page-specific rendering. Animations or changing data can produce different frames; disable or await them when stable output matters.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The script errors during launch or deployment

Ensure the package is installed and the selected browser binary is available in the runtime. A local development machine and a production container may not have identical browser dependencies or access to external assets. Check the error output from launch(), verify the deployment environment’s browser setup, and close the browser in a finally block after handling failures.

PNG looks unexpectedly large

Full-page captures and high device-scale factors produce more pixels and can increase file size and memory use. Reduce the viewport or scale factor where acceptable, or capture just the required element. If PNG is required, JPEG quality controls do not apply to it.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you want an HTTP call rather than managing a browser process, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns an image or PDF; its documented options include full-page capture, element selection, viewport presets, waits, custom CSS or JavaScript, and caching. See the ScreenshotNeo API documentation for parameters and response details.

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 request failed: ${res.status}`);
const imageBytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', imageBytes));

This example requests the default image output and writes the returned bytes to a file. Change the target URL as needed; use the API documentation for format selection and other options. The API also accepts the parameter names used by other screenshot APIs, which can ease a migration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; each cleanup step can be turned off.
  • Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers identify the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.

Sign up for 1,000 free screenshots a month with no card.

Which method should you choose?

Use Puppeteer or Playwright when your Node.js application needs direct control over a local browser, page lifecycle, HTML content, and in-memory screenshot bytes. Use a screenshot API when you prefer to make a request instead of operating the browser setup yourself. The right choice depends on where the page is rendered and how much control your application needs; the documented capture APIs do not establish a general performance winner between Puppeteer and Playwright.

Frequently Asked Questions

Does Node.js convert HTML to PNG without a browser?

For HTML that needs normal browser layout, CSS, fonts, images, or JavaScript, use a browser renderer such as Puppeteer or Playwright. A screenshot API is another option if you do not want to manage the browser process yourself.

Can I use the PNG screenshot as an upload buffer?

Yes. Omit the screenshot path and use the returned image data; Playwright documents a Buffer return, and Puppeteer returns image data when no path is supplied.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.