How to Pipe a Puppeteer Screen Recording to a File
Record a Puppeteer page to MP4 with the current `page.record()` API, or pipe its stream into a Node.js file writable with reliable cleanup.
Use Puppeteer’s current page.record() API. For a direct file, pass {path: 'recording.mp4'}. To control the destination stream yourself, pipe the returned recording into a Node.js writable, stop the recording, and wait for the file stream to finish before using the file. The current API produces MP4. The older page.screencast() API is obsolete.
Choose direct-to-file or a writable stream
Use the documented path option when you only need a recording saved to a path. Use pipe() when your application needs to manage the destination writable or stream flow. page.record() returns a ScreenRecording, a readable stream with a pipe(destination) method and a stop() method.
| Need | Approach |
|---|---|
| Save one recording to a file | page.record({path: 'recording.mp4'}) |
| Control the writable destination | page.record(), then pipe to a Node.js writable |
| Start new code with the older API | Do not: page.screencast() is marked obsolete; use page.record() |
Prerequisites and version check
- Use a Puppeteer release whose installed API and types expose
Page.record()andScreenRecording. The current API reference consulted for this guide showed Puppeteer 25.12.0; the separateScreenRecordingclass reference showed 25.11.0. Check the documentation and typings for the version installed in your project because signatures can differ. - Run the script in Node.js with a writable destination directory and enough disk space for the recording.
- This captures a Puppeteer page (a browser tab), not the entire desktop.
See the Page.record() API reference and ScreenRecording API reference.
Option 1: save directly to a file
This is the simplest documented path. Start recording after the page is ready, perform the browser actions you want captured, and stop the recorder before closing the browser.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', {waitUntil: 'networkidle0'});
const recorder = await page.record({path: 'recording.mp4'});
try {
await page.click('body');
await page.waitForTimeout(1000);
} finally {
await recorder.stop();
}
} finally {
await browser.close();
}
The path option is shown in Puppeteer’s official example. The actions between page.record() and recorder.stop() form the recording interval. Awaiting stop() before closing the browser gives Puppeteer the opportunity to finish recording.
Option 2: pipe the recording into a Node.js file stream
The example below composes the documented ScreenRecording.pipe() and stop() methods with Node.js filesystem streams. It waits for the writable to finish and propagates a write failure, so downstream code does not treat a partial file as complete.
import puppeteer from 'puppeteer';
import {createWriteStream} from 'node:fs';
import {finished} from 'node:stream/promises';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', {waitUntil: 'networkidle0'});
const recorder = await page.record();
const output = createWriteStream('recording.mp4');
const outputFinished = finished(output);
recorder.pipe(output);
try {
// Put the interactions and waits you want recorded here.
await page.click('body');
await page.waitForTimeout(1000);
} finally {
await recorder.stop();
await outputFinished;
}
} finally {
await browser.close();
}
Attach finished(output) before piping so the completion/error promise is observing the writable while it is active. Stop the recorder first, then await writable completion. Do not read, upload, or report the output file ready until that promise resolves.
If browser actions fail, the finally block still stops the recorder and waits for the destination. If the output itself errors, finished() rejects; the script surfaces the failure instead of silently claiming success. In production code, decide how to handle a partially written file after such an error, such as deleting it or retaining it for diagnosis.
Recording flow and practical details
- Navigate to the page and wait for the content you need. Choose an appropriate navigation condition; pages with ongoing network activity may never reach a network-idle condition.
- Start recording with
await page.record(), or supplypathfor direct output. - Perform the actions to capture: clicks, scrolling, navigation, or application-specific waits.
- Call and await
recorder.stop()even if an action throws. - For a manually piped output, wait for the writable to finish successfully before consuming the MP4.
- Close the browser after recording has stopped and output handling has completed.
Use an .mp4 filename for the current API’s MP4 output. The API material supplied for this implementation documents a path option and a pipeable stream; it does not establish additional recording options such as a configurable frame rate or quality setting. Check your installed Puppeteer API before relying on options beyond those documented there.
Errors and troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
page.record is not a function or a type error |
The installed Puppeteer version or typings do not expose this API. | Check the installed package’s API reference and types. Upgrade to a release that supports Page.record(), or use the API available in that version. |
| The MP4 is missing or incomplete | The browser was closed before recording stopped, the script did not await stop, or a piped writable had not finished. | Await recorder.stop() before closing the browser. With a writable, also await finished(output). |
| The output stream reports an error | The destination path may not exist, permissions may deny writing, the disk may be full, or the stream may have failed. | Confirm the parent directory exists and is writable, check available disk space, and await the stream completion promise so the error is surfaced. |
| Recording contains no expected interaction | The action happened before recording started or after it stopped. | Keep the action between the awaited page.record() call and recorder.stop(). |
| Navigation wait hangs | A site may keep requests open, so a network-idle condition may not occur. | Use a navigation condition suited to the page, or wait for a specific selector or state instead. |
| Legacy screencast setup requires FFmpeg or yields WebM | The code uses obsolete page.screencast(), whose documented legacy behavior differs. |
For new code, move to page.record() and follow its MP4 API reference. Do not assume legacy browser-version notes apply to page.record(). |
Performance, reliability, and cost
Recording writes video data, so output duration and page activity affect how much data must be handled and stored. Keep the recording interval limited to the actions you need, stream to the destination when that fits your application, and ensure the destination can accept the data. The supplied API references give no performance benchmarks or recording-size guarantees, so measure storage and runtime for your own pages and workloads.
For reliability, stop recording in a finally path, wait for file completion, and treat stream errors as failures. If recording is part of a job queue, only mark the job complete after both stop and output completion succeed. Puppeteer’s recording is tied to a page in its browser; it does not capture the operating system desktop.
The research sources specify no Puppeteer recording price, fixed resource cost, or benchmark. Account for the browser process, runtime, and storage in your own deployment rather than assuming a fixed cost.
Or skip the browser setup
If your goal is a still screenshot of a page rather than a video recording, ScreenshotNeo returns an image or PDF from one API request. It is a screenshot API and MCP server; it does not replace Puppeteer screen recording or produce a video.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for parameters and options. Cookie banners are accepted and removed before capture, along with supported consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. An MCP server lets AI agents use screenshot, page-information, and PDF tools. 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 and get 1,000 screenshots a month with no card.
FAQ
Does this capture the whole desktop?
No. Puppeteer records a page, which is one browser tab or extension background page.
Can I upload the recording directly to another destination?
The returned recording exposes a stream pipe method. Pipe it to a writable supported by your application, and handle that destination’s completion and errors.
Should I use page.screencast() for a new implementation?
No. The current Puppeteer documentation marks it obsolete and directs developers to page.record().
When should I choose a screenshot API instead?
Choose one when you need a still image or PDF of a webpage, not a timed video of browser interactions.


