Which Approach Do You Need?
| Context | Approach | Limitation |
|---|---|---|
| Browser (client-side) | html2canvas | Same-origin only, approximate rendering |
| Node.js (server-side) | Playwright / Puppeteer | Requires Chromium binary (~150 MB) |
| Any URL, serverless | SnapAPI REST | API 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.