Tutorial··8 min read

How to Take Website Screenshots in Node.js

Three real ways to capture website screenshots in Node.js — from full headless browser control with Puppeteer and Playwright to a single API call with SnapRender. Here's the code for each, with the trade-offs explained honestly.

1

Puppeteer

Puppeteer is Google's official Node.js library for controlling headless Chrome. It downloads a compatible Chromium binary automatically, gives you full browser control, and has a massive community. It's the industry standard — but it's heavy.

puppeteer
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 800 });
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'screenshot.png', fullPage: true });
  await browser.close();
})();

Watch out for:

  • Downloads ~170MB Chromium binary on first install
  • Chrome memory leaks in long-running processes
  • Brutal cold starts in serverless environments (Lambda, Cloud Functions)
  • Cloudflare and bot-protection sites will block headless Chrome

2

Playwright

Playwright is Microsoft's modern alternative. It supports Chromium, Firefox, and WebKit from a single API, auto-waits for content, and has native fullPage: true support. Cleaner API than Puppeteer, but same infrastructure overhead.

playwright
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage({
    viewport: { width: 1280, height: 800 }
  });
  await page.goto('https://example.com');
  await page.screenshot({ path: 'screenshot.png', fullPage: true });
  await browser.close();
})();

Better than Puppeteer, but still:

  • ~400MB browser download on first install
  • Multi-browser support adds complexity you may not need
  • Same Cloudflare/bot-detection issues as Puppeteer
  • Not serverless-friendly — cold starts exceed timeout limits

3

SnapRender API (one fetch call)

No browser to install, no binary to manage, no Chrome process to babysit. Send a POST request with the URL, get back the screenshot as binary. Works from any Node.js environment — serverless, containers, CI/CD, cron jobs.

SnapRender API1 fetch call
const response = await fetch('https://api.snaprender.dev/v1/screenshot', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ url: 'https://example.com' })
});

const buffer = Buffer.from(await response.arrayBuffer());
require('fs').writeFileSync('screenshot.png', buffer);

That's the entire working script. Uses the built-in fetch API available in Node.js 18+. Zero dependencies.


Full-page screenshots, custom viewports, JPEG quality

Most screenshot use cases need more than a basic viewport capture. Here's how to get a full-page screenshot at a specific resolution with JPEG compression:

full-page + jpeg
const response = await fetch('https://api.snaprender.dev/v1/screenshot', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://example.com',
    full_page: true,
    viewport: { width: 1440, height: 900 },
    format: 'jpeg',
    quality: 85
  })
});
ParameterTypeDescription
full_pagebooleanCapture entire scrollable page, not just the viewport
viewportobjectSet width and height in pixels (default 1280x800)
formatstring"png" or "jpeg" (default png)
qualityintegerJPEG quality 1-100 (default 80, ignored for PNG)

Express endpoint and S3 upload

Two common patterns: wrapping the screenshot in an Express route, and uploading the result to S3 for archival.

Screenshot as an Express endpoint

Build your own screenshot microservice in under 20 lines. Pass a URL as a query parameter and get the image streamed back.

express endpoint
const express = require('express');
const app = express();

app.get('/screenshot', async (req, res) => {
  const { url } = req.query;
  if (!url) return res.status(400).json({ error: 'url required' });

  const response = await fetch('https://api.snaprender.dev/v1/screenshot', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SNAPRENDER_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ url, full_page: true })
  });

  const buffer = Buffer.from(await response.arrayBuffer());
  res.set('Content-Type', 'image/png');
  res.send(buffer);
});

app.listen(3000);

Upload to S3

Capture and archive screenshots directly to an S3 bucket — useful for monitoring, compliance, or building a visual diff pipeline.

s3 upload
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');

// 1. Take the screenshot
const response = await fetch('https://api.snaprender.dev/v1/screenshot', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ url: 'https://example.com' })
});

// 2. Upload to S3
const s3 = new S3Client({ region: 'us-east-1' });
await s3.send(new PutObjectCommand({
  Bucket: 'my-screenshots',
  Key: 'captures/example.png',
  Body: Buffer.from(await response.arrayBuffer()),
  ContentType: 'image/png'
}));

Method comparison

FeaturePuppeteerPlaywrightSnapRender
Setup time5-10 min5-10 min30 seconds
DependenciesChromium (~170MB)Chromium (~400MB)None (fetch)
Full-pageYesYesYes
Cloudflare bypassNoNoYes
Serverless-readyDifficultDifficultYes
CostFree + infraFree + infra$9/mo for 1,500

Skip the browser setup

100 free screenshots per month. No Chromium to install. No credit card.

Get your API key

Frequently asked questions

The easiest way is using a screenshot API like SnapRender. It takes a single fetch call — no browser installation, no Puppeteer setup, no Chrome binary management required.

Playwright is generally better for new projects. It auto-waits for content, supports multiple browsers out of the box, and has a cleaner API. Puppeteer is more mature and has a larger ecosystem, but requires more manual wait logic.

Yes. Both Puppeteer and Playwright support fullPage: true in their screenshot methods. SnapRender supports full_page in the API request body. All three capture the entire scrollable page.

You need a headless browser. Puppeteer and Playwright both run real Chromium instances that execute JavaScript before capturing. SnapRender runs a managed browser in the cloud that waits for JS to finish.

Standard headless browsers get blocked by Cloudflare's bot detection. SnapRender has built-in Cloudflare bypass via FlareSolverr — add use_flaresolverr: true to your request.