How to convert HTML to JPG

A JPG of a page is a browser screenshot saved with lossy compression. It is the right format for image heavy pages and the wrong one for tables of text. Here is how to produce one and how to judge the result.

To convert HTML to JPG, render the page in a browser, capture it as an image, and save that capture with JPEG compression. The rendering part is identical to converting HTML to PNG; only the last step differs.

That last step matters more than it sounds. JPEG is lossy and it is tuned for photographs, not for the sharp edges of letters and table borders.

A table captured as JPG at quality 60, showing blocking around the letters and the cell borders.
A table captured as JPG at quality 60, showing blocking around the letters and the cell borders.

Before you convert HTML to JPG, pick the format

Page content Better format Reason
Tables, text, flat colour PNG Lossless, sharp edges, often smaller
Photographs, gradients JPG Much smaller at the same perceived quality
Logos and diagrams SVG Scales without any resampling
Transparent background PNG JPG has no alpha channel

If someone asked for a JPG because that is the word they know for "image", the answer is often PNG. If the page is a photo collage or a moodboard, JPG is genuinely correct.

Method 1: a headless browser

This is the only route that produces a JPG directly, at the quality you choose, without a second conversion.

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:/pages/invite.html');
await page.waitForLoadState('networkidle');
await page.screenshot({
  path: 'invite.jpg',
  type: 'jpeg',
  quality: 90,
  fullPage: true,
});
await browser.close();

Three options carry the result. type: 'jpeg' selects the encoder, quality controls the compression, and deviceScaleFactor decides whether text survives being zoomed.

Method 2: devtools, then convert

Browser devtools capture to PNG only. Right click an element in the Elements panel and choose Capture node screenshot, or run Capture full size screenshot from the command menu.

Then convert the PNG to JPG in any image tool. This adds a step and costs nothing in quality if you keep the JPEG quality high.

For a one-off image this is still the fastest route, because there is nothing to install.

Method 3: canvas in the page

If the export has to happen inside the page, draw the DOM to a canvas and export that canvas as JPEG.

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

The second argument of toDataURL is quality, from 0 to 1. Leave it out and browsers pick a default that is usually lower than you want.

Remember that this library repaints the DOM rather than screenshotting it, so unusual CSS can render differently from the browser.

Method 4: a converter page

Paste the HTML and take the image out. The HTML to image tool renders the markup and captures the result, with no file to save first.

HTML pasted into the image tool, rendered below the input and ready to capture.
HTML pasted into the image tool, rendered below the input and ready to capture.

Quality settings, in practice

Quality is a number between 1 and 100 and the useful range is narrow.

  • 95 and above. File size climbs sharply, visible gain is close to zero.
  • 88 to 92. The working default for a page with text and charts.
  • 75 to 85. Acceptable for photographs, visibly soft on letters.
  • Below 75. Blocking around text edges is obvious. Do not ship it.

If the file is too large at quality 90, the fix is usually to reduce the capture width rather than the quality. Halving the dimensions saves far more than dropping ten quality points.

There is one exception worth knowing. A page with a large photographic background compresses well at 80, because there are no sharp edges for the encoder to smear.

Judge by content, not by a rule. Open the output at full size and look at the smallest text on the page. If the letters have a visible halo, raise the quality and try again.

The problems that show up in the image

Fallback fonts. The capture fired before the web font loaded. Await document.fonts.ready before screenshotting, or embed the font.

Missing images. Relative paths do not resolve for the renderer. Base64 the images and they always travel.

Black background. The page had a transparent body and JPEG has no alpha, so the encoder filled it. Set an explicit background colour in CSS.

Empty charts. The script had not drawn yet. Wait for the specific element rather than a fixed delay.

A card with a transparent background exported as JPG, filled solid behind the content.
A card with a transparent background exported as JPG, filled solid behind the content.

What the image cannot do

A JPG is a fixed crop of one moment. Everything below the capture edge is gone, and every number in it is frozen at the time you pressed the button.

That is fine for a chat message where the point is that people see something without clicking. It is wrong for a report someone will read carefully.

The symptoms are consistent. Someone asks for a figure that is cut off. Someone quotes a number that changed last week. Someone tries to sort a column in an image.

Screenshot versus live page sets out the full comparison.

Sending both

The practical arrangement is an image for the glance and an address for the detail.

Paste the HTML into a NOS document and it renders as a page with its own link. The link unfurls into a preview card in chat, so the channel still shows something visual.

A NOS document link pasted into a chat channel, unfurled into a preview card with a title and image.
A NOS document link pasted into a chat channel, unfurled into a preview card with a title and image.

Then edits to the page do not require a new image or a new message. You click the text, fix it, and the link people already have points at the corrected version. Turning HTML into a link covers that step on its own.

Questions people ask

How do I convert an HTML page to JPG?

Render the page in a browser and capture it, then save the capture as JPG. A headless browser does this with a type option set to jpeg. In devtools you get a PNG and convert it afterwards.

Why does the text look fuzzy in my JPG?

JPG compresses in blocks and smears the boundary between sharp colours, which is exactly what text edges are. Raise quality to about 90, capture at double density, or use PNG instead for pages that are mostly type.

Does JPG support transparency?

No. Any transparent background becomes solid, usually black unless the renderer fills it. If you need a cut out background, PNG or SVG is the format to use.

What quality setting should I use?

Around 90 for a page with text and charts. Below 80 the artefacts around letters become visible. Above 95 the file grows quickly with almost no visible gain.

Keep reading