Images

How to Convert HTML to JPG or PNG (Browser Tool, Puppeteer and html2canvas Compared)

November 9, 2026 · 8 min read

To convert HTML to JPG or PNG you have three realistic options, and they fail in different ways. The quickest is a browser-based HTML to JPG and PNG converter: paste the markup, set a pixel size, download. The second is html2canvas, a JavaScript library that redraws part of a page you already have onto a canvas. The third is Puppeteer, which drives headless Chrome from Node and screenshots whatever it renders.

Which one you want comes down to two questions: is it a self-contained snippet (a social card, a quote graphic, a certificate) or a live page that fetches data and runs scripts, and does it need to happen once, by hand, or hundreds of times in a pipeline? Below: a short code sample for each method, the four things that break most often (web fonts, cross-origin images, foreignObject quirks, lazy loading), and which format and pixel size to export for cards and Open Graph images.

Three methods at a glance

The browser tool and Puppeteer render with a real browser engine, so modern CSS (flexbox, grid, gradients, custom properties) comes out the way the browser draws it; html2canvas re-implements CSS on a canvas and documents the properties it cannot draw.

MethodWhere it runsCaptures a live URL?Install neededBest for
Browser tool (paste HTML)Your browser tabNo, you paste markupNoneOne-off cards, OG images, inline-CSS templates
html2canvasInside your own web pageOnly the page it is loaded innpm packageA download-as-image button for a chart or share card
PuppeteerNode on a server or in CIYes, any URL or HTML stringNode plus a Chrome downloadBatch generation, live pages, JavaScript-built layouts

Method 1: paste HTML into a browser tool

The free HTML to Image converter on this site is the no-install route from HTML code to a PNG, JPEG or WEBP file. It has three input tabs: HTML Code (pre-filled with a sample card; Reset restores it), Upload HTML (pick a .html or .htm file; its text lands in the editor and the tab shows a Loaded note with the file name) and Custom CSS (rules applied to both the preview and the export). Everything, including reading the uploaded file, happens in your browser tab; nothing is sent to a server.

Under Advanced Image Options, open by default, you choose the format (PNG, JPEG or WEBP; PNG selected), a scale of 1x, 2x or 3x (2x selected), a quality from 10 to 100 for JPEG and WEBP (90 by default; greyed out for lossless PNG), a background of White, Transparent or a custom colour, and a width and height in pixels, 800 by 400 to start. Four presets fill the size: Twitter Card 1200 by 628, OG Image 1200 by 630, Square 800 by 800 and Story 1080 by 1920. The button reads Generate & Download plus the format name.

Your markup goes into a foreignObject inside an SVG sized at width times scale by height times scale, with a viewBox of the plain width and height, so the layout is composed at your CSS size and rasterised at the multiplied size: 2x is real resolution, not upscaling. The SVG becomes a data URL, is loaded into an image element, painted onto a canvas and read back with toDataURL. No JavaScript in your HTML runs: not in the export, nor in the preview, a sandboxed iframe without script permission. The preview can otherwise fetch remote images and fonts; the export cannot, so trust the preview for layout, not for assets.

Method 2: html2canvas, when the HTML is already on your page

html2canvas is for the download-this-card button inside an app you control: it walks the DOM of an element that is already rendered, reads the computed styles and redraws them with the canvas API, so it captures only what is in the current document and opens no URL.

The call takes the element and an options object: scale multiplies the output resolution (default: the device pixel ratio), useCORS requests cross-origin images with CORS headers, and backgroundColor null keeps transparency instead of the default white fill. Coverage is the limit, because it redraws CSS itself: the documentation lists unsupported properties including box-shadow, filter and mix-blend-mode, and it does not render iframes or plugin content; if your card leans on those, use another method.

  • import html2canvas from 'html2canvas';
  • const canvas = await html2canvas(document.querySelector('#card'), { scale: 2, useCORS: true, backgroundColor: null });
  • const link = document.createElement('a');
  • link.download = 'card.png';
  • link.href = canvas.toDataURL('image/png'); // or canvas.toDataURL('image/jpeg', 0.9)
  • link.click();

Method 3: Puppeteer, when you need a real browser on a server

Puppeteer launches headless Chrome from Node, so it renders exactly what Chrome renders: external fonts and remote images load, scripts run, and you can point it at a URL or hand it an HTML string. It is the tool for a hundred OG images from one template, or for a page that assembles itself with JavaScript.

In the sample, deviceScaleFactor is the browser tool's Scale: the layout stays 1200 by 630 and the file is 2400 by 1260. page.screenshot writes PNG unless you pass type jpeg or webp, quality applies only to those two, fullPage captures beyond the viewport and omitBackground gives a transparent PNG instead of Chrome's white page. The cost is operational: Puppeteer downloads its own Chrome and needs a Node runtime that can launch a browser process, which rules out static and most shared hosting.

  • import puppeteer from 'puppeteer';
  • const browser = await puppeteer.launch();
  • const page = await browser.newPage();
  • await page.setViewport({ width: 1200, height: 630, deviceScaleFactor: 2 });
  • await page.setContent(html, { waitUntil: 'networkidle0' }); // or page.goto(url, { waitUntil: 'networkidle0' })
  • await page.evaluateHandle('document.fonts.ready');
  • await page.screenshot({ path: 'card.png' }); // or { path: 'card.jpg', type: 'jpeg', quality: 90 }
  • await browser.close();

When each method fails, and the fix

Four problems account for most mismatches between screen and file; cause and fix differ by method.

  • Web fonts. The browser tool's export is an SVG loaded as an image, which cannot fetch anything, so a Google Fonts link never arrives and the text falls back to the next font in your stack (or the browser's default face). Unstyled text differs too: the exported body defaults to Arial, the preview's does not. Fix: a system font stack, or a base64 @font-face rule in Custom CSS, which applies to both. html2canvas uses the fonts the page has loaded, so await document.fonts.ready first; Puppeteer waits for the same promise, as in the sample.
  • Cross-origin images. The browser tool cannot load a remote image; convert it to a data URI and paste that. html2canvas needs useCORS true and a host that sends Access-Control-Allow-Origin; without the header the image is skipped, and allowTaint true draws it but toDataURL then throws a SecurityError. Puppeteer loads images like any browser.
  • foreignObject quirks. Inside the SVG your markup is parsed as XML, not HTML: void tags such as br, img and hr must be self-closed, a bare ampersand must be written as &, and HTML-only entities such as   are undefined (use  ). A whole document with a doctype fails too; paste the fragment. The exported body also has 16 px of padding and only a min-height, so content below your Height is clipped although the scrolling preview shows it; set body padding 0 in Custom CSS for a full-bleed card.
  • Lazy-loaded content. An img with loading lazy well below the viewport has not been fetched when Puppeteer screenshots: scroll to the bottom and call page.waitForNetworkIdle, or wait for a specific selector. html2canvas draws the DOM as it is, so an unloaded image is an empty box; the browser tool never fetches, so paste final markup with data URIs.

JPG, PNG or WebP for cards and OG images

Cards are text on flat colour, the worst input for a lossy codec: JPEG puts faint halos around crisp letters and hard edges, so for a typical card PNG is sharper and often not much larger. JPEG earns its place when a photograph or large gradient dominates, or when an uploader refuses PNG; keep quality at 90 or above, because artefacts show on text first. WebP is usually the smallest at comparable quality and keeps transparency, but some CMS uploaders still reject it.

JPEG has no alpha channel, so a transparent background never survives, and what replaces it differs by method: the browser tool warns you and fills the canvas white; Puppeteer ignores omitBackground for JPEG and gives Chrome's white page; html2canvas with backgroundColor null flattens transparent pixels onto black when you call toDataURL('image/jpeg'), so keep its default white fill. Real transparency means PNG or WEBP in the tool, a PNG screenshot with omitBackground in Puppeteer, or a PNG with backgroundColor null in html2canvas.

PlacementPixelsIn the browser toolFormat
Open Graph (Facebook, LinkedIn, Slack)1200 × 630OG Image presetPNG, or JPEG at 90+ over a photo
X / Twitter summary_large_image1200 × 628Twitter Card presetSame as OG; one 1200 × 630 file serves both
Instagram or LinkedIn square post1080 × 1080Type it in (the Square preset is 800 × 800)PNG or WebP
Instagram or Facebook Story1080 × 1920Story (9:16) presetJPEG or WebP (photo-heavy)
YouTube thumbnail1280 × 720Type it inJPEG or PNG

Frequently Asked Questions

A .jpeg. The download is always named html-to-image plus the format's extension (.png, .jpeg or .webp), whatever the uploaded file was called. JPG and JPEG are the same format, so if an uploader insists on the three-letter extension, rename the file.

The preview is a normal iframe document that can fetch a linked font; the export is an SVG loaded as an image, which cannot, so the text falls back to the next font in your stack or the browser's default face. Use a system font stack or a base64 @font-face rule in the Custom CSS tab, which applies to both.

Only Puppeteer, because it is a real browser. The browser tool renders static markup in both the export and the preview iframe, which has no script permission. html2canvas runs no scripts either, but it captures a DOM your page's scripts have already built, so a finished chart is drawn.

The browser refused to load the SVG built from your markup, and the usual cause is markup that is not well-formed XML: an unclosed br, img or hr, a bare ampersand, an   entity, or a whole document with a doctype. Self-close void tags, escape ampersands and paste the fragment. The message also names remote images and fonts; those more often leave a blank box in an otherwise successful download, so inline them as data URIs. A canvas too large for the browser shows a different message: Couldn't build the image — try a smaller size, or switch format to PNG.

Pick the OG Image preset and leave Scale at 2x: the layout is composed at 1200 by 630 and rasterised at 2400 by 1260, real resolution rather than upscaling. In Puppeteer set deviceScaleFactor 2 on a 1200 by 630 viewport; in html2canvas pass scale 2. Export PNG unless the card is mostly photo.

Related Tools