October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
JavaScript

How to Save Multiple Puppeteer Screenshots Without Overwriting Files

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Give each capture a different destination path. A constant value such as screenshots/page.png tells Puppeteer to write every image to the same file, so each successful call replaces the previous one. Build the filename from a counter, run ID, URL slug, or collision-resistant suffix, create the directory first, and await navigation and screenshots in order.

The basic fix: generate a path inside the loop

Puppeteer’s ScreenshotOptions.path is optional and identifies the output file. Relative paths are resolved from the current working directory, and the image format is inferred from the extension. Move filename construction into the iteration so every call receives a new path.

import puppeteer from 'puppeteer';
import { mkdir } from 'node:fs/promises';
import { join } from 'node:path';

const outputDir = join(process.cwd(), 'screenshots');
await mkdir(outputDir, { recursive: true });

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  const urls = [
    'https://example.com/one',
    'https://example.com/two',
    'https://example.com/three',
  ];

  for (const [index, url] of urls.entries()) {
    await page.goto(url, { waitUntil: 'networkidle2' });
    const filename = `page-${String(index + 1).padStart(3, '0')}.png`;
    await page.screenshot({
      path: join(outputDir, filename),
      fullPage: true,
    });
  }
} finally {
  await browser.close();
}

This produces page-001.png, page-002.png, and page-003.png. The padded number keeps alphabetic and chronological order aligned. The counter is appropriate when the input order is stable and each run uses a fresh directory.

Why page.screenshot() keeps replacing your image

A path is not a collection name; it is one filesystem destination. If the loop repeatedly passes screenshots/page.png, the operating system opens that same file for writing each time. Node.js documents that its normal asynchronous file write replaces an existing file by default, which is why no Puppeteer error is required for the overwrite to occur.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Screenshot calls are asynchronous. Always await each call when order matters. Puppeteer coordinates screenshot operations within a browser context, but it cannot infer that two identical paths should be preserved as separate versions.

Choose a naming strategy

Strategy Collision resistance Reproducibility Sortability Best use
Zero-padded counter Low across repeated runs High High One run in a new directory
Run ID plus counter High between runs High within a run High Scheduled jobs and archived captures
Random suffix Very high Low Moderate Several workers sharing one directory
Exclusive create (wx) Guaranteed by the write layer Depends on name generation Depends on prefix Never replacing an existing path

Separate every run with an identifier

If rerunning the script must preserve earlier output, put a timestamp-like identifier in the directory or filename. For example, use screenshots/20260929T125922Z/page-001.png. Remove punctuation from an ISO timestamp so it is safe and easy to sort. A run ID prevents one invocation from replacing another while retaining deterministic numbering inside the run.

Use a random suffix for shared directories

A timestamp alone can collide when concurrent processes start during the same time unit. Add a cryptographically strong or otherwise collision-resistant suffix while retaining a readable prefix, such as home-004-a8f31c.png. This is less reproducible, so record the mapping between input URL and generated name if another system will consume the files.

Guarantee non-replacement with exclusive creation

Puppeteer’s path option writes the capture directly; exclusive creation is a separate Node.js filesystem concern. To reject an existing destination, request the image as a buffer and write it with the wx flag:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
import { writeFile } from 'node:fs/promises';

async function saveWithoutReplacing(page, path) {
  const buffer = await page.screenshot({ fullPage: true });
  await writeFile(path, buffer, { flag: 'wx' });
}

If the write fails with EEXIST, generate another name and retry. Do not silently switch back to a normal write, because that restores overwrite behavior.

Capture only what you intend

Viewport versus full page

page.screenshot() normally captures the current viewport. Set fullPage: true when the desired artifact is the entire scrollable document. Full-page mode does not automatically load an infinite-scroll feed: implement scrolling and a stopping condition before capturing if content appears only after scrolling.

A single rendered element

When the requirement is a component rather than a page, locate it and use its ElementHandle.screenshot(). This avoids producing large files containing unrelated content and lets your filename describe the component or test case.

File formats

Use an extension that matches the required output, such as .png or .jpeg; Puppeteer infers the screenshot type from that extension. Keep one extension policy for a batch so downstream processing does not need to guess formats.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Safe URL-derived filenames

URL text can contain slashes, query delimiters, reserved device names, or characters that are invalid on a target operating system. Never concatenate a raw URL into a path. Extract a short hostname or label, replace anything outside a conservative set such as letters, numbers, dots, underscores, and hyphens, and cap its length. Keep the counter or run ID separate so two URLs with similar labels still receive unique names.

function safeLabel(value) {
  return value
    .replace(/[^a-z0-9._-]+/gi, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 80) || 'page';
}

const host = new URL(url).hostname;
const filename = `${safeLabel(host)}-${String(index + 1).padStart(3, '0')}.png`;

Ordering, concurrency, and reliability

Sequential capture

The loop in the example waits for navigation and then for the screenshot before moving to the next URL. This is simplest, keeps numbering deterministic, and limits simultaneous browser work. Use an appropriate readiness condition—networkidle2 is useful for many pages, but pages with persistent analytics connections may never become truly idle; add a bounded timeout and an explicit selector or delay when the page has a known readiness signal.

Parallel workers

Parallelism can improve throughput, but several workers writing predictable names can race. Give each worker a unique run or worker prefix, or use random names plus exclusive creation. Limit concurrency to the memory and CPU available to Chromium, and preserve the input index in metadata if completion order differs from input order.

Always close Chromium

Put browser.close() in a finally block. A failed navigation, invalid URL, or filesystem exception should not leave Chromium processes running and consuming resources on later jobs.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Operational checklist

  • Create the destination with mkdir(..., { recursive: true }).
  • Generate a unique filename for every iteration.
  • Sanitize any URL-derived label.
  • Await page.goto() and page.screenshot() in the intended order.
  • Choose .png, .jpeg, or another extension deliberately.
  • Use fullPage: true only for document-length images.
  • For shared directories, combine unique names with an exclusive-create check.
  • Close the browser in finally.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common failures

Only one file exists

Cause: every iteration uses the same path. Fix: include the loop index, run ID, or random suffix in the filename and log the resolved path before capturing.

ENOENT or a missing-directory error

Cause: the parent directory does not exist or the process is running from an unexpected working directory. Fix: create it recursively and log process.cwd(); prefer an absolute path built with join().

EEXIST with exclusive writes

Cause: the generated name already exists. Fix: catch the error, add a new suffix, and retry. This is the expected protection provided by wx, not a browser failure.

Screenshots are blank or incomplete

Cause: capture started before the relevant UI rendered, or a lazy/infinite feed was never activated. Fix: wait for a specific selector, an application-ready condition, or a bounded delay; scroll deliberately for infinite content; then capture.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Sale
JavaScript and jQuery: Interactive Front-End Web Development
  • JavaScript Jquery
  • Introduces core programming concepts in JavaScript and jQuery
  • Uses clear descriptions, inspiring examples, and easy-to-follow diagrams

Navigation times out

Cause: the page has slow resources, a blocked request, or connections that remain open. Fix: set a suitable timeout, choose a less strict readiness condition, and verify the URL independently. Do not treat a timeout as a valid screenshot; record the failure and continue or retry according to your job policy.

Files from simultaneous jobs still collide

Cause: both jobs use the same counter and directory. Fix: isolate directories by run, add worker IDs or random suffixes, and use exclusive creation when replacement is unacceptable.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. It removes cookie/consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with the response identifying the page verdict and billing status. Its MCP tools—take_screenshot, get_page_info, and capture_pdf—let Claude, Cursor, or another MCP client request captures without your own Chromium process.

One request returns an image or PDF. Choose a unique output filename in your own loop while ScreenshotNeo handles rendering:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo documentation for options such as full-page capture, CSS selectors, device presets, custom JavaScript, waits, blocking requests, caching, signed links, asynchronous jobs, webhooks, and bulk capture of up to 100 URLs per call. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Frequently Asked Questions

Does changing the screenshot format prevent overwriting?

No. The extension selects the format, but uniqueness comes from the complete path. Two calls to the same page.png or page.jpeg destination can still replace one another.

Can I reuse one Puppeteer Page for many URLs?

Yes. Reusing a page is compatible with separate files; navigate, await the desired readiness condition, and pass a new path for each URL.

How should I resume a partially completed batch?

Persist the input index and output path, then skip records whose files pass your validity check. Use run-specific directories so a resumed job cannot overwrite a prior run.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Quick Recap

SaleBestseller No. 1
HTML and CSS: Design and Build Websites
HTML and CSS: Design and Build Websites
HTML CSS Design and Build Web Sites; Comes with secure packaging; It can be a gift option
$14.18
SaleBestseller No. 2
Web Design with HTML, CSS, JavaScript and jQuery Set
Web Design with HTML, CSS, JavaScript and jQuery Set
Brand: Wiley; Set of 2 Volumes
$35.05
SaleBestseller No. 3
SaleBestseller No. 5
JavaScript and jQuery: Interactive Front-End Web Development
JavaScript and jQuery: Interactive Front-End Web Development
JavaScript Jquery; Introduces core programming concepts in JavaScript and jQuery; Uses clear descriptions, inspiring examples, and easy-to-follow diagrams
$22.80

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.