How to convert HTML to WebP in a browser
Render an HTML element to a canvas, encode it as WebP, and handle browser support, cross-origin assets, quality, and downloads.
HTML is markup, not an image. To convert HTML to WebP in a browser, first render the DOM content you want into pixels, then encode those pixels with canvas.toBlob(callback, "image/webp", quality). Check the returned Blob’s MIME type: if WebP encoding is unsupported, the browser may give you PNG instead.
This guide captures one element using html-to-image, downloads the resulting WebP, and covers browser support, cross-origin assets, quality choices, and common failures. The library renders a DOM node through SVG foreignObject, so inspect the result in the browsers and with the page assets you need to support. See the MDN documentation for toBlob() and the html-to-image project documentation.
1. Install the DOM capture library
For a project using npm, install html-to-image:
npm install html-to-image
Use a bundler such as Vite, webpack, or another setup that supports JavaScript module imports. The browser must be able to load this module and the page assets you want to include.
2. Add a capture target and a download button
Give the target content a stable ID. The example is a complete HTML document body; save it as an HTML file in a project configured to bundle the import, or adapt the markup and script to your app.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTML to WebP</title>
<style>
body { font: 16px system-ui, sans-serif; margin: 2rem; }
#capture { box-sizing: border-box; width: 640px; padding: 32px;
color: #182230; background: #f1f5f9; border-radius: 16px; }
#capture h1 { margin-top: 0; }
</style>
</head>
<body>
<main id="capture">
<h1>A card to capture</h1>
<p>The browser renders this DOM element before exporting it.</p>
</main>
<button id="download" type="button">Download WebP</button>
<p id="status" role="status"></p>
<script type="module" src="/src/main.js"></script>
</body>
</html>
3. Render the element and encode it as WebP
In src/main.js, ask the library for a Blob in WebP format. Confirm the browser returned image/webp before naming the download with a .webp extension. The quality value is between 0 and 1 for lossy formats; this example uses 0.9 as a starting point, not a universal best setting.
import { toBlob } from 'html-to-image';
const target = document.querySelector('#capture');
const button = document.querySelector('#download');
const status = document.querySelector('#status');
button.addEventListener('click', async () => {
button.disabled = true;
status.textContent = 'Rendering…';
try {
const blob = await toBlob(target, {
type: 'image/webp',
quality: 0.9,
pixelRatio: 1
});
if (!blob) throw new Error('The browser did not produce an image Blob.');
if (blob.type !== 'image/webp') {
throw new Error(`WebP encoding is unavailable; the browser returned ${blob.type || 'an unknown type'}.`);
}
const objectUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = objectUrl;
link.download = 'capture.webp';
link.click();
link.remove();
status.textContent = `Downloaded ${blob.type} (${blob.size} bytes).`;
// Keep the URL alive long enough for the browser to start the download.
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
} catch (error) {
status.textContent = error instanceof Error ? error.message : String(error);
} finally {
button.disabled = false;
}
});
If you need a preview instead of a download, set an image element’s src to the object URL and revoke it only after the preview is no longer needed. An object URL is temporary; do not revoke it while the user may still open or save the image.
4. What the conversion does
- Select the content. A DOM node capture exports the chosen element, not necessarily the entire document. Choose an element whose size and layout are settled before capture.
- Render DOM and styles. The library builds an image representation from the node and its styles. Rendering details such as SVG
foreignObject, fonts, and assets can differ across browser engines. - Encode canvas pixels. The canvas encoding step receives a MIME type and optional quality. Per MDN, if the requested type is unsupported, the browser can export PNG instead.
- Check and use the Blob. Inspect
blob.type, then download, preview, upload, or otherwise consume the Blob. Do not infer format from the filename.
5. Configure fidelity, dimensions, and quality
Choose the capture area
Pass the exact DOM node you need. For a whole page, a library may offer a full-document helper, but long pages can create very large canvases and memory use. Capturing a focused component is usually easier to size and inspect. Keep the layout viewport and element dimensions stable while rendering.
Set output scale
The example sets pixelRatio: 1 to keep output dimensions close to the rendered element’s CSS dimensions. Increasing the pixel ratio creates more pixels and can improve detail on high-density displays, but it also increases encoding time and memory. Check the resulting image dimensions rather than assuming a CSS pixel maps to one output pixel.
Pick lossy or lossless output
WebP supports lossy and lossless encoding. A quality number is a lossy-quality control; it does not guarantee a specific file size or visual result. For photographs, lossy output may be a reasonable tradeoff. For UI screenshots, text, diagrams, logos, and line art, use a lossless path if the library and browser expose it, or inspect the output closely for artifacts around sharp edges.
MDN describes lossy WebP as typically 25–35% smaller than visually similar JPEG and lossless WebP as typically 26% smaller than PNG. These are typical comparisons, not promised savings for a particular capture. A WebP file can be larger than the source or an alternative encoding depending on content and settings. See MDN’s image format guide and Google’s WebP FAQ.
Wait for page content
Before capturing, wait for images and fonts that matter. For images, use img.decode() where available; for fonts, document.fonts.ready can indicate the document’s font loading has settled.
await document.fonts.ready;
await Promise.all(
[...document.querySelectorAll('#capture img')].map((img) =>
img.decode().catch(() => undefined)
)
);
const blob = await toBlob(document.querySelector('#capture'), {
type: 'image/webp',
quality: 0.9
});
Decoding can fail for a broken image, so the example allows capture to continue; decide whether your own workflow should instead report missing assets as an error.
6. Browser support and cross-origin constraints
Feature-test the export path
Displaying a WebP file and encoding a canvas as WebP are different capabilities. Browser and version support can vary, and a library’s DOM rendering can have additional compatibility limits. Test the exact browser, library, and page combination you plan to support, then inspect blob.type.
MDN documents that unsupported requested formats fall back to PNG. If your app requires WebP specifically, treat a different MIME type as an unsupported-browser condition or choose a server-side encoder fallback. Do not save a PNG Blob with a .webp filename.
Keep the canvas origin-clean
External images or other cross-origin resources may taint a canvas. Canvas export can then fail with a SecurityError. The remote server must permit the applicable cross-origin access, and the resource must be loaded under the relevant CORS rules. Setting an image’s crossOrigin attribute alone does not grant permission; the server’s response must allow the request. If you cannot change the remote server, use an authorized same-origin proxy or omit that asset.
Do not assume every CSS background, embedded font, video frame, or SVG will export identically. Validate the exact assets and browser behavior. MDN’s canvas toBlob() reference describes the origin-clean requirement and the possible security exception.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The output is PNG despite requesting WebP | The browser does not support WebP canvas encoding, or the export path fell back. | Check blob.type. Feature-test the target browser. Use a PNG fallback or encode on a server if WebP is mandatory. |
SecurityError during export |
A cross-origin image or other resource made the canvas non-origin-clean. | Serve the asset with suitable CORS permission, fetch it through an authorized same-origin proxy, or exclude it. |
| Image is blank or missing content | Capture ran before images, fonts, or client-rendered content were ready, or the selected node was wrong. | Confirm the selector, wait for the app’s render state, await relevant image decoding and font readiness, then capture again. |
| Text, shadows, or layout differ from the page | DOM-to-image rendering support for styles, fonts, and SVG foreignObject differs by browser. |
Test in the target browser, ensure fonts are loaded and assets accessible, and simplify unsupported effects if necessary. |
| Output looks soft or file is unexpectedly large | Pixel ratio, dimensions, and quality tradeoffs may not fit the content. | Inspect pixel dimensions and compare quality settings. Reduce scale for smaller files; use lossless output for crisp graphics where available. |
| Download does nothing or preview breaks | The object URL was revoked too early, or browser download behavior requires a user gesture. | Start the download directly from the button click and retain the object URL until the download or preview has had time to use it. |
| Library cannot be imported | The example uses an ES module import but the project is not configured to bundle or serve modules. | Install the package in the app’s build setup, or use the library’s documented browser distribution for your setup. |
8. Performance, reliability, and cost
Browser capture uses client CPU and memory. Large elements, high pixel ratios, full-page captures, and multiple concurrent encodes increase work and can produce memory-heavy canvases. Capture one item at a time when the page is large, limit the output dimensions to what the user needs, and release object URLs after use.
Reliability depends on the browser rendering the same content you expect: wait for the app and its assets, feature-test WebP encoding, and handle failed exports and fallback MIME types. The in-browser method has no screenshot API usage charge, but it consumes the visitor’s device resources and may need a fallback for unsupported browsers or inaccessible assets.
Or skip the browser setup
If you need a screenshot of a live URL rather than an image Blob created inside the visitor’s browser, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API docs for its options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
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}`);
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Response headers identify the page verdict and whether the request was billed.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents and MCP clients. - 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Every feature is on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Can I convert an HTML string directly to WebP?
Not with the canvas API alone. The browser must render HTML into pixels first. Put the content in the DOM, render it with a DOM-to-image approach, and then encode the resulting canvas.
Does toBlob() guarantee a WebP file?
No. Unsupported requested formats can fall back to PNG. Check the returned Blob’s type before saving it as WebP.
Will this capture the entire page?
The example exports one selected element. Capturing a whole document requires a library or implementation that handles full-page dimensions; verify its output and memory needs for long pages.
Can a browser preserve the original HTML as editable content in a WebP?
No. WebP stores raster image data, not editable DOM or CSS. Keep the source HTML separately if you need to edit the content later.


