Capture guides
HTML to Image: How to Render HTML to PNG, JPG, or WebP
June 22, 2026 · 6 min read · Grabbit Team

To turn HTML into an image you have three real options: a client-side library that rasterizes a DOM node, an online converter for one-off jobs, and a screenshot API that renders your HTML at a URL and returns a hosted PNG, JPG, or WebP. The right one depends on whether the HTML is already on screen in a browser or whether you need to generate the image on a server, on demand, at scale.
This guide covers all three and shows where each breaks down.
The three ways to convert HTML to an image
| Approach | Runs where | Best for | Limitation |
|---|---|---|---|
Client-side library (html-to-image, dom-to-image) | In the browser | Exporting a chart or card the user is viewing | Cannot run on a server; complex CSS and cross-origin images break |
| Online converter (paste HTML) | Their server | One-off, manual conversions | Manual, not automatable, not for production |
| Screenshot API (render a URL) | Their server, via your code | Automated server-side generation at scale | Renders a URL, so host your HTML first |
If the HTML is already rendered in front of a user, a client-side library is the fastest path. If you need to generate images from a server, on a schedule, or for thousands of pages, you want the API approach.
Option 1: Rasterize a DOM node in the browser
The html-to-image npm package generates an image from a DOM node using HTML5 canvas and SVG. It is the most-starred answer for the in-browser case:
import { toPng } from 'html-to-image';
const node = document.getElementById('invoice');
const dataUrl = await toPng(node);
const link = document.createElement('a');
link.download = 'invoice.png';
link.href = dataUrl;
link.click();
This is excellent for "let the user download what they see," like exporting a generated chart, a receipt, or a shareable card. The catch is that it works only where a browser and the DOM already exist. It cannot render a URL from your backend, and because it reconstructs the node through canvas rather than using the browser's real renderer, web fonts, CSS filters, and cross-origin images frequently come out wrong or blank.
Option 2: Online converters
Tools like PDFCrowd, CloudConvert, and Templated let you paste HTML and download a PNG or JPG. They are fine for a one-time visual, but they are manual: there is no clean way to wire them into a build step, a webhook, or a per-user image pipeline. Use them to sanity-check a layout, not to generate images in production.
Option 3: Render your HTML at a URL and capture it with an API
For anything automated, the reliable pattern is to render your HTML at a real URL and screenshot it with an API that runs actual Chromium. This is how dynamic Open Graph images are generated: the API loads the page exactly like a browser, so your CSS, web fonts, and JavaScript all render correctly, and you get back a hosted image URL.
The key thing to understand: a URL-based screenshot API renders a URL, not a raw HTML string. So the first step is to host your HTML somewhere the API can reach it. That is usually a route in your own app.
Step 1: Host your HTML template at a route
Create a page that renders your HTML and reads any dynamic data from query parameters. Keep the body sized to the image you want:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
width: 1200px;
height: 630px;
display: flex;
align-items: center;
justify-content: center;
background: #0f172a;
font-family: system-ui, sans-serif;
}
.title { color: #f8fafc; font-size: 64px; font-weight: 700; }
</style>
</head>
<body>
<div class="title">{{ title }}</div>
</body>
</html>
Host it at a route like https://yoursite.com/card. Swap the placeholder for your framework's templating so the same page can render any title.
Step 2: Capture the rendered page
Point the screenshot API at that URL and pick your format:
curl https://api.grabbit.live/v1/grabs \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://yoursite.com/card?title=Ship+Faster",
"width": 1200,
"height": 630,
"format": "webp"
}'
The response gives you a hosted image you can use directly:
{
"id": "grb_01jx...",
"status": "done",
"image_url": "https://cdn.grabbit.live/grabs/grb_01jx....webp",
"width": 1200,
"height": 630,
"format": "webp",
"bytes": 31180,
"execution_ms": 880
}
Set "format" to png, jpeg, or webp depending on what you need (see the format section below). If your template draws content with JavaScript after load, add "delay_ms": 500 so the script finishes before the capture fires, or "selector": "#card" to wait until that element appears. To capture a tall page beyond the viewport, set "full_page": true.
Step 3: Cache the result
The image URL is stable, so store it the first time you generate it:
async function htmlToImage(title: string): Promise<string> {
const url = new URL('https://yoursite.com/card');
url.searchParams.set('title', title);
const res = await fetch('https://api.grabbit.live/v1/grabs', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GRABBIT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: url.toString(),
width: 1200,
height: 630,
format: 'webp',
}),
});
const grab = await res.json();
return grab.image_url; // store this; reuse it on every later request
}
Save image_url in your database or CMS and serve it from there. The API only runs again when the underlying content changes, so one template produces unlimited images and you pay once per unique image.
PNG vs JPG vs WebP: which format
- PNG keeps text crisp and supports transparency. Best for UI, cards, and anything with sharp edges or a transparent background.
- JPG (use
"format": "jpeg") is smaller for photographic content but adds compression artifacts around text. Reach for it when the image is mostly a photo and file size matters. - WebP usually gives the smallest file at the same visual quality, which makes it a strong default for social cards and stored thumbnails. Every modern browser and social platform supports it.
All three come from the same render, so switching is a one-word change in the request body.
When to use which approach
Use the client-side html-to-image library when the content is already on screen and the user is exporting it themselves. Use an online converter for a quick one-off. Use a screenshot API whenever the generation needs to be automated, server-side, and reliable, because it renders in a real browser and removes the brittleness of canvas-based rasterization and the burden of running headless Chromium yourself.
For the closely related job of generating a unique social preview for every page, see how to generate dynamic OG images from any URL. To capture an existing live page rather than your own template, see how to screenshot a website from a URL.
FAQ
- How do I convert HTML to an image?
- Three approaches work. For HTML already in the browser, a client-side library like html-to-image rasterizes a DOM node to a PNG or SVG using canvas. For one-off conversions, an online converter accepts pasted HTML. For automated, server-side generation, host your HTML at a URL and capture it with a screenshot API, which renders the page in real Chromium and returns a hosted PNG, JPG, or WebP.
- What is the html-to-image npm package?
- html-to-image is a browser library that generates an image from a DOM node using HTML5 canvas and SVG. It runs client-side, so it captures what is already rendered on the page. It is a good fit for exporting a chart or card the user is looking at, but it cannot render a URL on a server, and complex CSS or cross-origin images can break the output.
- How do I convert HTML to a PNG on the server?
- Render the HTML at a public URL, then call a screenshot API with that URL and format set to png. The API loads the page in a real headless browser and returns a hosted PNG, so you get pixel-accurate output without running Chromium yourself. Set width and height to control the canvas, or full_page to capture the whole document.
- Can I turn an HTML string directly into an image?
- Not with a URL-based screenshot API directly. Screenshot APIs render a URL, so the reliable pattern is to host your HTML template at a route (for example /card) and pass data as query parameters, then point the API at that URL. This keeps the template in your codebase and lets one template produce unlimited images.
- What format should I use, PNG, JPG, or WebP?
- Use PNG for sharp text and transparency, JPG for photographic content where a smaller file matters, and WebP when you want the smallest file at good quality. WebP typically produces the smallest file for the same visual result, which is why it is a good default for social cards and stored thumbnails.
Capture any website with one API call
Get a free test key and capture your first screenshot in two minutes.
Written by
Grabbit Team
Screenshots as a service
The team behind Grabbit, the screenshot API for developers and AI agents. We write about web capture, rendering, and automating screenshots at scale.
Keep reading
How to Generate Dynamic OG Images from Any URL
Generate a unique Open Graph image for every page by pointing a screenshot API at your HTML template. One template, zero design work, scales to any number of pages.
Jun 12, 2026 · 4 min read
How to Screenshot a Website from a URL (No Browser Needed)
Skip the headless browser setup. Learn the three ways to capture a screenshot from a URL, and when a screenshot API beats Puppeteer or Playwright for production workloads.
Jun 15, 2026 · 5 min read

How to Take a Scrolling Screenshot (Every Device and Browser)
How to take a scrolling screenshot on Windows, Mac, iPhone, Android, Chrome, and Firefox, plus how to capture long scrolling pages automatically with an API.
Jun 20, 2026 · 5 min read