How to convert HTML to PNG

A PNG of a page is a screenshot taken by a browser. The four routes differ in who drives the browser and how much of the page you capture. All of them freeze the result, which is the point and also the cost.

To convert HTML to PNG, render the page in a browser and capture it. Every method does that; they differ in who drives the browser.

PNG is the right format here rather than JPG, because pages are mostly flat colour, text and lines. PNG keeps edges sharp and supports transparency. Converting HTML to JPG covers the cases where the tradeoff goes the other way.

The devtools context menu on a selected element, with Capture node screenshot highlighted.
The devtools context menu on a selected element, with Capture node screenshot highlighted.

Four ways to convert HTML to PNG

Method Install Full page Automatable
Devtools capture None Yes No
Headless browser Node and a browser Yes Yes
html2canvas in the page A script tag Partly Yes
Converter page None Yes No

Choose by how often you need it. A one-off image comes out of devtools in fifteen seconds. A nightly report image needs a headless browser.

Method 1: devtools, for a single image

This is the highest fidelity option available without installing anything, because the capture is done by the same engine that drew the page.

  1. Open the page and press F12, or Cmd+Option+I on a Mac.
  2. In the Elements panel, right click the element you want and choose Capture node screenshot. The PNG contains that element at its full height, including the part below the fold.
  3. For the whole page, press Ctrl+Shift+P inside devtools and type screenshot, then choose Capture full size screenshot.

The node capture is the underrated one. It crops to exactly one card, table or chart with no cropping afterwards.

Method 2: a headless browser

For anything repeated, drive a browser from a script. The pattern is load, wait, capture.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({
  viewport: { width: 1200, height: 800 },
  deviceScaleFactor: 2,
});
await page.goto('file:///C:/reports/weekly.html');
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'weekly.png', fullPage: true });
await browser.close();

Two settings decide whether the output is usable. deviceScaleFactor: 2 produces a retina density image, which is what stops text looking soft when someone zooms. fullPage: true captures past the fold.

To capture one element rather than the page, locate it and screenshot the locator instead of the page.

Method 3: html2canvas, inside the page

When the button has to live in the page itself, a client side library redraws the DOM onto a canvas and exports that canvas as a PNG.

const canvas = await html2canvas(document.querySelector('#report'), { scale: 2 });
const url = canvas.toDataURL('image/png');

Understand what it is doing. It is not a browser screenshot; it is a reimplementation that reads computed styles and paints them. Complex CSS, some filters and cross origin images can come out wrong.

Test it against the devtools capture of the same element before shipping it.

Method 4: a converter page

Paste the HTML, get an image back. This is the route when the HTML is not already live anywhere and you do not want to save a file first.

The HTML to image tool does this in the browser. Paste, render, capture.

HTML pasted into the converter, with the rendered page below it ready to capture.
HTML pasted into the converter, with the rendered page below it ready to capture.

The four things that go wrong

Web fonts arrive late. The capture fires before the font loads and the image shows the fallback. In a script, await document.fonts.ready before screenshotting. Better, embed the font so there is no request to lose.

Images do not travel. Anything on a relative path or a cross origin address may be blank in the capture. Base64 the images and the problem disappears.

Charts have not drawn. A chart script needs a frame or two after load. Wait for the canvas to have a non-zero size, or wait for a specific element to appear.

The scrollbar is in the shot. A full page capture at a fixed viewport can bake the scrollbar into the right edge. Widen the viewport or hide overflow for the capture.

The same card captured twice, once before the web font loaded and once after.
The same card captured twice, once before the web font loaded and once after.

Size and density

A PNG at one times density looks soft on every phone made in the last decade. Capture at two times and let the display downsample.

  • Set deviceScaleFactor to 2 in a headless browser, or scale: 2 in html2canvas.
  • Devtools captures at the density of your screen, so a retina laptop already gives you this.
  • PNG is lossless, so a dense page of text can be a large file. If it is a photograph rather than a page, JPG will be much smaller.

When the image is the wrong deliverable

An image is a frozen crop. That is fine for a thumbnail in a chat message and wrong for a report with numbers in it.

Three symptoms that you want a page instead. The content extends past one screen. Someone will ask what a number was last week. Anything in it sorts, filters or animates.

Screenshot versus live page sets out the comparison in full.

The practical middle ground is to send both. A PNG so the message shows something at a glance, and a link so the detail behind it is one click away.

Paste the HTML into a NOS document and the page gets its own address. The link stays correct as the content changes, because editing the text does not move the address.

Turning HTML into a link is that step alone. The HTML to image tool gives you the PNG from the same markup, so you are not maintaining two sources.

When a figure is corrected, you fix it once in the document. The link people already have points at the corrected page, and you re-capture the image only if the picture itself needs to change.

Questions people ask

What is the fastest way to convert HTML to PNG?

Open the page, open devtools, and use Capture node screenshot on the element you want. It captures the rendered element at full size, including parts below the fold, with no install and no code.

How do I capture the whole page and not just the visible part?

Use Capture full size screenshot in the devtools command menu, or in a headless browser pass the fullPage option. A plain screenshot key only captures what is on screen.

Why are the fonts wrong in my PNG?

The renderer took the shot before the web font finished loading, or the font was never reachable. Wait for document.fonts.ready in a script, or embed the font so it does not depend on a network request.

Should I send a PNG or a link?

Send a PNG when the content is final and small enough to read at a glance. Send a link when there are numbers that change, content below the fold, or anything interactive. A PNG cuts off and goes stale; a link does not.

Keep reading