ScreenshotNeo

BlogHow-to

How to Convert HTML to JPG, JPEG, or Other Image Formats

Learn reliable ways to render HTML as JPG, JPEG, PNG, or WebP with Playwright, Puppeteer, html2canvas, wkhtmltoimage, and ScreenshotNeo.

By the ScreenshotNeo team29 September 20269 min read

How to Convert HTML to JPG, JPEG, or Other Image Formats

Short answer: HTML must be rendered before it can become a JPG or JPEG. For browser-accurate output, use Playwright or Puppeteer to load the page and call a screenshot API with type: 'jpeg'. Use html2canvas when the conversion runs inside a browser and you only need a DOM-based export. Use wkhtmltoimage when an existing command-line pipeline depends on it. For a managed endpoint, ScreenshotNeo converts a URL to JPEG, PNG, WebP, or PDF with one request.

What “convert HTML to JPG” actually means

HTML is markup, not a pixel format. A converter has to perform these steps:

HTML is rendered first, then encoded into an image format.
HTML is rendered first, then encoded into an image format.
  1. Load the HTML, CSS, fonts, images, scripts, and other resources.
  2. Lay out the document in a browser or rendering engine.
  3. Choose a viewport, page height, device-pixel scale, and capture area.
  4. Encode the rendered pixels as JPEG, PNG, WebP, or another output format.

The right method depends on where conversion runs and how closely the result must match what a visitor sees. Browser automation provides the strongest fidelity for modern CSS and JavaScript. A browser-side library is simpler for a “Download this card” button, but it reconstructs the DOM rather than taking a true browser screenshot.

Choose a method

Method Best for Strengths Important limits
Playwright Server-side automation and production rendering Chromium, Firefox, and WebKit; viewport, element, full-page, JPEG, PNG, WebP, quality, and scale controls Requires browser binaries and an execution environment
Puppeteer Projects already built around Chromium automation Simple navigation and screenshot API; JavaScript runs naturally Chromium-focused; browser process adds operational overhead
html2canvas In-browser “download this element” features No server renderer; exports a canvas with toDataURL() DOM reconstruction, CSS gaps, cross-origin image and iframe restrictions
wkhtmltoimage Existing shell or legacy pipelines One command and options for cookies, headers, cropping, and resource access Validate its rendering engine, JavaScript behavior, and security posture for your pages
ScreenshotNeo Managed URL-to-image conversion One HTTP request; clean captures; JPEG, PNG, WebP, and PDF; no browser deployment Requires an API key and network access

For current API details, see the Playwright screenshot guide, Page screenshot API, Puppeteer screenshot guide, Puppeteer Page.screenshot API, and html2canvas documentation.

Convert HTML to JPEG with Playwright

Playwright is the general-purpose choice when the image must match a real browser. It supports viewport screenshots, a selected element, or the full scrollable page. JPEG quality is configurable, and scale controls whether dimensions use CSS pixels or device pixels.

Install

npm install playwright
npx playwright install chromium

Capture a URL as a JPG

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 1
});

await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({
  path: 'page.jpg',
  type: 'jpeg',
  quality: 85,
  fullPage: true,
  scale: 'css'
});

await browser.close();

Use fullPage: false for only the viewport. Use fullPage: true for the complete scrollable document. JPEG quality is normally an integer from 0 through 100; lower values reduce file size and increase artifacts. A screenshot cannot be JPEG and have transparency, so use PNG or WebP when transparent backgrounds matter.

Capture one element

const card = page.locator('#invoice');
await card.screenshot({
  path: 'invoice.jpg',
  type: 'jpeg',
  quality: 90
});

Load an HTML string

const html = `<!doctype html>
<html><body><h1>Invoice</h1></body></html>`;
await page.setContent(html, { waitUntil: 'networkidle' });
await page.screenshot({ path: 'invoice.jpg', type: 'jpeg', quality: 90 });

Wait for fonts, images, and application data

await page.goto('https://example.com/report', { waitUntil: 'domcontentloaded' });
await page.evaluate(() => document.fonts.ready);
await page.waitForSelector('#report-ready');
await page.waitForTimeout(300);
await page.screenshot({ path: 'report.jpg', type: 'jpeg', quality: 85, fullPage: true });

networkidle is useful for pages that finish loading, but analytics, polling, and open connections can prevent a stable idle point. For those pages, wait for a specific readiness selector or application state and use a short, deliberate delay.

Convert HTML to JPEG with Puppeteer

Puppeteer is a strong fit when your service already uses Chromium automation.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
await page.screenshot({
  path: 'page.jpg',
  type: 'jpeg',
  quality: 85,
  fullPage: true
});
await browser.close();

You can pass an element handle to elementHandle.screenshot(), set cookies before navigation, add request interception, or call page functions to reveal content before capture. The official guide documents navigation followed by page.screenshot; the API can also return image data or a base64 string instead of writing a file.

Convert an element in the browser with html2canvas

html2canvas traverses the DOM and builds a canvas representation. Its documentation explicitly says the result is not an actual screenshot and may not be 100% accurate to the real representation. It is convenient for a user-facing export button when the content is same-origin and uses supported CSS.

import html2canvas from 'html2canvas';

const element = document.querySelector('#receipt');
const canvas = await html2canvas(element, {
  scale: 2,
  useCORS: true,
  backgroundColor: '#ffffff'
});

const dataUrl = canvas.toDataURL('image/jpeg', 0.9);
const link = document.createElement('a');
link.download = 'receipt.jpg';
link.href = dataUrl;
link.click();

Useful html2canvas options

  • scale: increase resolution, at the cost of memory and a larger output.
  • useCORS: true: request CORS-enabled images when the remote server permits it.
  • x, y, width, and height: crop the rendered region.
  • data-html2canvas-ignore: mark nodes that must not appear in the result.
  • backgroundColor: choose a solid background; use null when transparency is required and supported by your export format.

Cross-origin images can taint the canvas. Cross-origin iframes cannot be rendered, and plugin content such as Flash or Java applets is unsupported. Browser same-origin policy still applies; html2canvas cannot bypass it.

Convert HTML from the command line with wkhtmltoimage

If an existing server or build pipeline already uses wkhtmltoimage, its documented form is:

wkhtmltoimage [OPTIONS] <input file> <output file>
wkhtmltoimage --quality 85 --width 1440 input.html output.jpg
wkhtmltoimage --cookie session abc123 https://example.com output.jpg

Available options vary by installed version and package. The Debian manpage documents resource access, cookies, headers, cropping, and related rendering behavior. Before standardizing on it, verify the binary’s JavaScript support, rendering engine, and security settings against the exact pages you need to render.

Control output dimensions, quality, and format

Requirement Recommended setting
Small photographic image JPEG quality 75–90; avoid text-heavy designs at very low quality
Sharp text, diagrams, or transparency PNG or WebP instead of JPEG
Retina artwork Use device-pixel scaling or a higher scale, then check memory use
Long page Use full-page capture and test maximum document height
Exact card dimensions Capture a locator or set explicit width and height in CSS

Fonts and external assets are common sources of differences. Wait for document.fonts.ready, ensure image requests have completed, and use a fixed viewport and timezone when comparing output between runs. Dynamic ads, rotating content, animations, and current timestamps can make otherwise identical captures differ.

Or skip the browser setup

ScreenshotNeo is a managed website screenshot API. Its endpoint accepts one GET request and returns a clean PNG, JPEG, WebP, or PDF. The service accepts cookie and consent banners like a visitor, then 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.

Cleanup before capture produces a usable page image without consent overlays and popups.
Cleanup before capture produces a usable page image without consent overlays and popups.

See the ScreenshotNeo API documentation for all options. A minimal JPEG request is:

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

The URL above follows the required API example; choose the output format and other capture options as documented for your request.

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
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 failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));

ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets and custom viewports, retina scale, PDF paper and margin controls, custom CSS and JavaScript, clicks, selector or network-idle waits, ad and tracker blocking, custom headers, cookies, user agents and authorization, timezone and geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed image links, 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 to make migration easier.

An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Plans include 1,000 shots per month free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Troubleshooting

The JPG is blank or only partly rendered

Cause: capture happened before data, fonts, lazy images, or client-side rendering finished. Fix: wait for a readiness selector, document.fonts.ready, image completion, or a short delay. For long pages, use full-page capture after the content is present.

Images or fonts are missing

Cause: blocked requests, incorrect relative URLs, authentication, or CORS restrictions. Fix: use absolute asset URLs, pass required cookies or headers, inspect failed requests, and ensure the asset server permits the origin. html2canvas cannot render cross-origin resources that violate canvas security rules.

Text looks different between runs

Cause: different fonts, viewport, device scale, timezone, animations, or changing data. Fix: install the same fonts, set a fixed viewport and timezone, disable animations with custom CSS, and capture a deterministic test page.

The page never reaches network idle

Cause: analytics, WebSockets, polling, or permanently open connections. Fix: wait for a specific selector or application event rather than global network idle, and block irrelevant requests where appropriate.

JPEG quality is poor or files are too large

Cause: quality is too low, device scale is too high, or a photographic page is being encoded losslessly. Fix: tune JPEG quality, use CSS-pixel scale when suitable, resize after capture, or choose WebP. Keep PNG for crisp text and transparency.

Cause: the target uses a bot check, CAPTCHA, authentication wall, or network policy. Fix: confirm authorization, provide required headers or cookies, and test from the same network as production. A managed capture service can report bot checks and failed loads in response headers; ScreenshotNeo does not bill those unsuccessful captures.

Performance, reliability, and cost

  • Browser startup: reuse a Playwright or Puppeteer browser process and create isolated pages or contexts per job.
  • Concurrency: limit parallel pages by CPU and memory; full-page and high-scale captures consume more memory.
  • Asset control: block ads, trackers, video, and other unnecessary resource types when they are not part of the image.
  • Caching: cache deterministic pages, but choose a TTL that matches how often content changes.
  • Retries: retry transient navigation and network errors with a bounded backoff; do not blindly retry authentication failures or CAPTCHAs.
  • Cost: self-hosted browser automation costs compute, browser maintenance, and engineering time. html2canvas shifts work to the user’s device. ScreenshotNeo bills only clean shots; cache hits and failed, blank, timed-out, or bot-blocked results are not billed.

FAQ

Is JPG different from JPEG?

No. They refer to the same JPEG image format; the shorter extension is common on systems with older three-character extension limits.

Can I convert a local HTML file?

Yes. Playwright, Puppeteer, and wkhtmltoimage can load a local file or HTML string. Ensure local asset paths resolve and that your deployment policy permits file access.

Can HTML become a vector image?

The methods here rasterize the page. Use a dedicated SVG or PDF workflow when you need scalable vector output.

Which method should I use for an automated service?

Use Playwright or Puppeteer when you need control inside your own infrastructure. Use ScreenshotNeo when you want a managed endpoint, cleanup of consent UI, usage reporting, and an MCP server without deploying browsers.

How do I capture only one component?

Use a locator or element screenshot in Playwright or Puppeteer, or pass the element to html2canvas in a browser. ScreenshotNeo supports CSS-selector element capture as an API option.