Back to blog

Capture guides

HTML to Image: How to Render HTML to PNG, JPG, or WebP

June 22, 2026 · 6 min read · Grabbit Team

HTML to Image: How to Render HTML to PNG, JPG, or WebP

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

ApproachRuns whereBest forLimitation
Client-side library (html-to-image, dom-to-image)In the browserExporting a chart or card the user is viewingCannot run on a server; complex CSS and cross-origin images break
Online converter (paste HTML)Their serverOne-off, manual conversionsManual, not automatable, not for production
Screenshot API (render a URL)Their server, via your codeAutomated server-side generation at scaleRenders 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