How to Set a Custom Filename for Puppeteer Screenshots
Set Puppeteer’s screenshot path to choose the filename, format, and destination. See runnable examples, naming patterns, and fixes for common path issues.
Set the path option in page.screenshot() to the filename and destination you want:
await page.screenshot({ path: 'screenshots/homepage.png' });
Puppeteer saves the image at that path and infers its format from the extension. A relative path is resolved from the process’s current working directory. If you omit path, Puppeteer returns image bytes instead of saving a file. See the ScreenshotOptions reference and Puppeteer screenshot guide.
1. Save a screenshot with a custom filename
Here is a complete Node.js example using Puppeteer. It opens a page, captures it, and saves a full-page PNG to a named file:
const puppeteer = require('puppeteer');
async function main() {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({
path: 'screenshots/example-homepage.png',
fullPage: true,
type: 'png'
});
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
The important setting is path. Include the directory, base name, and extension. Choose a stable filename for a one-off capture; generate a distinct name for repeated captures if each result must be retained.
2. Choose a path and image format
Puppeteer infers the screenshot type from the filename extension. Use an extension that matches the format you want, such as .png or .jpeg. The path is interpreted by the Node.js process, not by the page being captured.
| Path example | Result | When to use it |
|---|---|---|
shot.png |
PNG in the current working directory | A simple local capture |
screenshots/shot.png |
PNG under a relative directory | A project output folder |
/tmp/shot.png |
PNG at an absolute path | A known machine-specific destination |
screenshots/shot.jpeg |
JPEG inferred from extension | When JPEG output is wanted |
In scripts, containers, and scheduled jobs, the working directory may differ from the directory containing the script. If the output must always go to a particular location, construct an absolute path from a known base directory.
3. Create the output directory first
Do not assume Puppeteer will create missing parent directories. Create them in your application before taking the screenshot:
const fs = require('node:fs/promises');
const path = require('node:path');
const puppeteer = require('puppeteer');
async function main() {
const outputDir = path.resolve(process.cwd(), 'screenshots');
await fs.mkdir(outputDir, { recursive: true });
const outputPath = path.join(outputDir, 'homepage.png');
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: outputPath, fullPage: true });
console.log(`Saved screenshot to ${outputPath}`);
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
path.resolve(process.cwd(), ...) makes the chosen base explicit. If your project has a different stable root, build the path from that instead.
4. Generate unique filenames for repeated captures
A fixed path is overwritten when you capture the same page again. For an archive or a batch job, create a filename from a sanitized label and a timestamp. Avoid using an arbitrary URL directly as a filename: URLs can contain slashes, query strings, reserved characters, or sensitive values.
const fs = require('node:fs/promises');
const path = require('node:path');
const puppeteer = require('puppeteer');
function safeFilenamePart(value) {
return value
.toLowerCase()
.replace(/[^a-z0-9-]+/g, '-')
.replace(/^-+|-+$/g, '') || 'page';
}
async function main() {
const outputDir = path.resolve('screenshots');
await fs.mkdir(outputDir, { recursive: true });
const label = safeFilenamePart('Example Homepage');
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const outputPath = path.join(outputDir, `${label}-${timestamp}.png`);
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: outputPath, fullPage: true });
console.log(outputPath);
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
If multiple workers might produce a screenshot at the same instant, a timestamp alone may not guarantee uniqueness. Add a request ID or another unique value, and ensure the filename does not include untrusted path segments.
5. Save an element screenshot to a custom path
For a specific element, use ElementHandle.screenshot() with the same path option:
const element = await page.$('#invoice');
if (!element) {
throw new Error('Could not find #invoice');
}
await element.screenshot({ path: 'screenshots/invoice.png' });
The destination and extension work the same way as for page.screenshot(). The element must exist and be visible enough to capture; wait for it if the page renders it asynchronously.
6. Keep the image bytes and choose the filename yourself
If you leave out path, page.screenshot() returns a Uint8Array. This is useful when your program needs to decide how to persist, upload, or process the image. You can still choose any filename when writing the bytes:
const fs = require('node:fs/promises');
const puppeteer = require('puppeteer');
async function main() {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
const imageBytes = await page.screenshot({ fullPage: true });
await fs.mkdir('screenshots', { recursive: true });
await fs.writeFile('screenshots/custom-name.png', imageBytes);
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
With this approach, your application controls persistence. Make sure the filename extension agrees with the screenshot format you requested or received.
7. Or skip the browser setup
ScreenshotNeo provides a website screenshot API: one GET request returns an image or PDF. The API can take a screenshot without installing or managing Puppeteer in your project. Its documentation describes the request options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Cookie banners are accepted or removed before the capture, along with known newsletter popups and chat widgets. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers identify the page verdict and billing status. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000. Learn more at ScreenshotNeo, then sign up for 1,000 free screenshots a month with no card.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No file appears | path was omitted, or the script wrote to a different working directory than expected. |
Set path explicitly and log process.cwd() or resolve an absolute output path. |
| Directory or file error | The parent directory does not exist or the process cannot write there. | Create the directory with fs.mkdir(..., { recursive: true }) and choose a writable location. |
| Unexpected image format | The extension and requested screenshot type do not agree, or the extension is missing. | Use a supported, matching extension and set type explicitly when needed. |
| Previous image was replaced | Each capture reused the same destination. | Use a unique timestamp, request ID, or page-specific sanitized name. |
| Element capture fails | The selector did not match an element or the element was not ready. | Wait for the selector, verify it exists, and then call element.screenshot(). |
| Screenshot is incomplete | The page was captured before its required content finished loading. | Wait for the relevant selector or page state before capturing; choose an appropriate navigation wait condition. |
9. Performance, reliability, and cost
- Filename choice has little capture cost. The path determines where Puppeteer writes the result; page loading, rendering, image size, and full-page capture are the larger operational factors.
- Control output size. Full-page images can be large. Use a viewport capture when the whole document is not required, and avoid retaining duplicate captures unnecessarily.
- Make output predictable. Resolve paths from a known directory, create it before capture, and use unique names for concurrent or repeated jobs.
- Handle failures in the caller. Put browser cleanup in a
finallyblock and handle navigation, capture, and file-writing errors. Retry only failures that are transient and safe to repeat. - Budget for the browser runtime. Puppeteer requires a browser environment and the associated operational work in your application. If you would rather send a URL to a hosted API, ScreenshotNeo has a free tier of 1,000 shots monthly and paid tiers from $5 for 3,000; see the API docs.
10. FAQ
Does Puppeteer have a separate filename option?
For a screenshot saved by Puppeteer, use the path option. There is no separate filename field needed.
Can I choose the folder as well as the filename?
Yes. Include the folder in path, and create that folder in your application if it does not exist.
Can I rename the file after capture?
Yes. Save the returned bytes yourself or rename the saved file using Node.js filesystem APIs. When possible, choosing the final path at capture time is simpler.
Which Puppeteer version should I check?
Use the API reference for the version installed in your project. The reviewed official reference currently identifies version 25.12.0; APIs can change between releases.


