ScreenshotNeo

BlogHow-to

How to Take a Screenshot of a Div with JavaScript

Capture a selected div as a downloadable image with html2canvas, handle browser limits, and automate reliable element shots with Playwright or ScreenshotNeo.

By the ScreenshotNeo team1 October 20267 min read

To capture a div in the browser, select it and pass the element to html2canvas. The library returns a Promise for a canvas that you can display or download as a PNG.

<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<script>
async function downloadDiv() {
  const element = document.querySelector('#capture');
  if (!element) throw new Error('Element #capture was not found');

  const canvas = await html2canvas(element);
  const link = document.createElement('a');
  link.download = 'capture.png';
  link.href = canvas.toDataURL('image/png');
  link.click();
}
</script>

Call downloadDiv() from a button click after the div has rendered. The target must exist when capture starts. Handle a rejected Promise so a failed capture does not leave the user without feedback.

1. Complete working example

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Download a div</title>
  <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
  <style>
    #capture { width: 420px; padding: 24px; background: white; color: #172033; border: 1px solid #d8deea; border-radius: 12px; font: 16px system-ui, sans-serif; }
    body { background: #f2f5f9; padding: 40px; }
  </style>
</head>
<body>
  <article id="capture">
    <h1>Monthly report</h1>
    <p>This card is the region exported as a PNG.</p>
  </article>
  <p><button id="save" type="button">Download PNG</button></p>
  <script>
    document.querySelector('#save').addEventListener('click', async () => {
      const button = document.querySelector('#save');
      const element = document.querySelector('#capture');
      if (!element) return;
      button.disabled = true;
      try {
        const canvas = await html2canvas(element, {
          backgroundColor: '#ffffff',
          scale: Math.min(window.devicePixelRatio || 1, 2),
          useCORS: true
        });
        const link = document.createElement('a');
        link.download = 'monthly-report.png';
        link.href = canvas.toDataURL('image/png');
        link.click();
      } catch (error) {
        console.error(error);
        alert('The card could not be captured. Check the browser console for details.');
      } finally {
        button.disabled = false;
      }
    });
  </script>
</body>
</html>

2. What html2canvas actually captures

html2canvas does not read the browser’s final pixel buffer. It walks the DOM and recreates a representation from the properties it understands. That explains why a capture can differ from the visible page: unsupported CSS, browser-only effects, fonts that have not loaded, and cross-origin resources can change the result. See the html2canvas documentation for the supported-property and security model.

Use this approach when the page itself needs an export button. It runs in the user’s browser and produces a canvas, data URL, or blob without a server.

3. Control the output

Resolution with scale

scale multiplies the output pixels. A value of 2 is useful for a retina-sized image; larger values consume more memory and can fail on large elements. Choose the smallest value that meets the display or print requirement.

Background and transparency

Set backgroundColor when a predictable background is required. If the design needs transparency, use the library’s transparent-background setting and ensure the target’s own CSS does not paint an opaque background.

Crop a region

For a rectangle inside the selected element, pass x, y, width, and height. Coordinates are relative to the document capture coordinates, so measure after layout has settled. Cropping reduces the canvas size and memory use.

Wait for layout and assets

await document.fonts.ready;
await Promise.all(
  [...document.images].filter(image => !image.complete)
    .map(image => new Promise(resolve => {
      image.addEventListener('load', resolve, { once: true });
      image.addEventListener('error', resolve, { once: true });
    }))
);
const canvas = await html2canvas(document.querySelector('#capture'));

Do this after data, fonts, images, and animations are ready. Pause CSS animations or add a capture-only class if a moving chart or blinking cursor must be stable.

4. Images, iframes, and origin restrictions

Cross-origin images can taint a canvas, making toDataURL() fail. useCORS: true helps only when the image server sends an appropriate CORS header. A configured proxy is another documented option; neither bypasses browser security policy. An inaccessible cross-origin iframe cannot be traversed and recreated by html2canvas. Host assets on the same origin, enable CORS on the asset server, or replace the external content with a capture-safe version.

5. Capture one element with Playwright

For tests, scheduled jobs, or server-side automation, use a real browser screenshot instead of rebuilding styles in a canvas. Playwright’s locator API captures the rendered element:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({ deviceScaleFactor: 2 });
await page.goto('https://example.com/report', { waitUntil: 'networkidle' });
await page.locator('#capture').screenshot({ path: 'report.png' });
await browser.close();

Playwright waits for the page you control and writes a file or returns a buffer. Its output reflects browser rendering more directly than html2canvas. See the locator screenshot API.

6. Choosing between the two methods

Requirement Use html2canvas Use Playwright
User clicks “download” in your page Yes; returns a canvas in the same tab Usually unnecessary
Pixel fidelity to browser rendering Limited by DOM/CSS support Strong; captures a rendered browser element
Cross-origin content Subject to CORS and iframe rules Browser context still follows web security, but automation can load the page as a visitor
CI visual regression Not the usual fit Yes; save deterministic files or buffers
Server-side scale Requires a user’s browser Requires managing browser workers

7. Troubleshooting

“Cannot read properties of null”

The selector matched nothing. Check the id or class, run capture after the component mounts, and fail with a clear message as shown above.

The image is blank or missing styles

Capture ran before data, fonts, images, or web fonts finished loading. Await those resources, remove loading placeholders, and pause animations.

toDataURL throws a security error

A cross-origin image or canvas tainted the output. Serve the asset with CORS, use useCORS when permitted, use a configured proxy, or keep the asset same-origin.

CSS looks different

html2canvas supports a subset of CSS. Check unsupported properties in its documentation and create a capture-specific style that uses supported equivalents.

The tab runs out of memory

Lower scale, crop with width and height, capture a smaller element, and avoid repeatedly retaining canvases. Release old canvas references after download.

Playwright captures the wrong state

Wait for the selector and application state explicitly, use a stable viewport and device scale factor, and disable time-dependent animations before calling screenshot.

8. Performance, reliability, and cost

  • Capture only the needed element; full-page canvases multiply memory and encoding time.
  • Use the lowest acceptable scale and prefer a blob or file workflow for large images.
  • Debounce repeated captures from resize or input events.
  • For automation, reuse a Playwright browser process but isolate pages and close them after each job.
  • Client-side html2canvas has no API charge, but it uses the user’s CPU and memory. Browser automation adds browser startup, storage, and worker costs.
  • Neither method bypasses bot checks, consent overlays, network failures, or origin policy. Treat a failed capture as a recoverable job and report the cause.

Or skip the browser setup

ScreenshotNeo captures a page or a selected element by CSS selector through one API request. It accepts cookie and consent banners before the capture, then removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. It also provides an MCP server for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API docs for selector capture and options such as full-page mode, viewport and device presets, retina scale, output format, custom CSS and JavaScript, waiting rules, request blocking, headers and cookies, timezone, geolocation, caching, signed links, async webhooks, bulk capture, and PDF output.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/report -o report.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/report"}, timeout=90)
r.raise_for_status()
open("report.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/report' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
require('node:fs').writeFileSync('report.webp', Buffer.from(await res.arrayBuffer()));

Plans include 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Can I capture a div without downloading a file?

Yes. Append the returned canvas to the document, convert it to a blob, or send the data URL to your own upload endpoint.

Will html2canvas capture a video frame?

Do not assume it will reproduce a cross-origin or actively changing video reliably. Draw a permitted frame to a same-origin canvas first, then capture that result.

Should I use PNG or JPEG?

PNG keeps text and transparency sharp. JPEG is smaller for photographic content but does not preserve transparency. WebP is another compact option when your delivery pipeline supports it.

Can I capture a div on a page I do not control?

In-page JavaScript is limited by the page’s origin and permissions. For a URL-based, server-driven capture, use a browser automation service such as ScreenshotNeo.