JavaScript Screenshot of a Webpage (2026)

Three approaches: html2canvas in the browser for client-side capture, Playwright or Puppeteer in Node.js for server-side, and SnapAPI REST for on-demand production screenshots.

JavaScripthtml2canvasPlaywright Node.jsSnapAPIApril 2026

Which Approach Do You Need?

ContextApproachLimitation
Browser (client-side)html2canvasSame-origin only, approximate rendering
Node.js (server-side)Playwright / PuppeteerRequires Chromium binary (~150 MB)
Any URL, serverlessSnapAPI RESTAPI key required, network latency

Browser: html2canvas

html2canvas renders your current page's DOM to a <canvas> element. It works for same-origin content — you can't screenshot an external URL from the browser due to CORS.

npm install html2canvas
# or via CDN: <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
import html2canvas from 'html2canvas';

// Screenshot the current page
const canvas = await html2canvas(document.body, {
  scale: 2,              // 2x resolution for retina
  useCORS: true,         // allow cross-origin images
  allowTaint: false,
  backgroundColor: '#ffffff',
  logging: false,
});

// Download as PNG
const link = document.createElement('a');
link.download = 'screenshot.png';
link.href = canvas.toDataURL('image/png');
link.click();

Screenshot a specific DOM element

// Screenshot just a card or report section
const element = document.getElementById('report-card');
const canvas = await html2canvas(element, {
  scale: 2,
  width: element.offsetWidth,
  height: element.offsetHeight,
  backgroundColor: null,  // transparent background
});
const dataUrl = canvas.toDataURL('image/png');
// dataUrl is a base64 PNG string
html2canvas limitations: Doesn't support CSS transforms, position: fixed elements, cross-origin fonts without CORS headers, or SVG foreignObject. For pixel-perfect results, use a server-side Playwright screenshot instead.

Node.js: Playwright (Recommended)

import { chromium } from 'playwright';

// Screenshot any URL — no CORS restrictions
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.setViewportSize({ width: 1280, height: 900 });

await page.goto('https://example.com', { waitUntil: 'networkidle' });
const buffer = await page.screenshot({
  fullPage: true,       // capture full scrollable page
  type: 'png',
});
await browser.close();

// buffer is a PNG Buffer — write to file or return from endpoint

Screenshot specific element by selector

const el = await page.locator('#hero-section');
const elementBuffer = await el.screenshot({ type: 'png' });

Node.js: Puppeteer

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 900, deviceScaleFactor: 2 });

await page.goto('https://example.com', { waitUntil: 'networkidle2' });
const buffer = await page.screenshot({ fullPage: true });
await browser.close();

SnapAPI REST: Any URL, No Binary

// Screenshot any URL from any JavaScript context (browser, Node, Deno, Edge)
const res = await fetch('https://api.snapapi.pics/v1/screenshot', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://example.com',
    fullPage: true,
    format: 'png',
    width: 1440,
    height: 900,
    deviceScaleFactor: 2,
    blockAds: true,
    blockCookieBanners: true,
    waitUntil: 'networkidle',
  }),
});
const buffer = Buffer.from(await res.arrayBuffer());

"Download screenshot" button (browser + SnapAPI backend)

// Frontend
document.getElementById('screenshot-btn').addEventListener('click', async () => {
  const urlToCapture = document.getElementById('url-input').value;

  const res = await fetch('/api/screenshot', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ url: urlToCapture }),
  });

  const blob = await res.blob();
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.download = 'screenshot.png';
  link.click();
});

// Backend (Express)
app.post('/api/screenshot', async (req, res) => {
  const { url } = req.body;
  const snap = await fetch('https://api.snapapi.pics/v1/screenshot', {
    method: 'POST',
    headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ url, fullPage: true }),
  });
  res.set('Content-Type', 'image/png');
  res.send(Buffer.from(await snap.arrayBuffer()));
});
SnapAPI free tier: 200 screenshots/month. Get your API key → No Chromium binary or browser process needed.