How to Use Puppeteer Tracing to Debug Slow Pages
Capture a Puppeteer trace, inspect slow work in Chrome DevTools, and repeat the capture to validate your fix.
Use Puppeteer’s page.tracing.start() and page.tracing.stop() to record browser activity around a slow navigation or interaction. Open the resulting JSON trace in Chrome DevTools’ Performance panel, zoom into the slow interval, and inspect the main thread with Call tree, Bottom-up, and Event log. A trace shows where recorded work happened; confirm a suspected cause by changing one thing and capturing again under the same conditions.
1. Choose the right recording
First determine when the slowdown occurs:
- During navigation or initial loading: capture a load recording around
page.goto(). - After the page is running: capture a runtime recording around the interaction, such as opening a menu or filtering a list.
Make the problem repeatable before recording. Keep the URL, action sequence, browser conditions, and test data as consistent as practical. Chrome’s Performance guidance recommends recording a consistently reproducible issue. See the Chrome recording guidance.
2. Capture a navigation trace with Puppeteer
This complete Node.js example starts tracing before navigation, stops after the page has loaded, and writes trace.json in the current directory.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.tracing.start({ path: 'trace.json' });
try {
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
} finally {
await page.tracing.stop();
}
} finally {
await browser.close();
}
Install Puppeteer in a Node project with npm install puppeteer. If the issue is a runtime interaction, load the page first, start tracing immediately before the action, perform it, then stop:
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.tracing.start({ path: 'interaction-trace.json' });
try {
await page.click('[data-testid="open-menu"]');
await page.waitForSelector('[data-testid="menu"]');
} finally {
await page.tracing.stop();
}
Starting just before the action keeps the recording focused. Choose an action and completion condition that match the actual user-visible delay; a selector appearing may not mean all relevant work has finished.
3. Configure the trace when needed
page.tracing.start() accepts a TracingOptions object. The commonly useful options are:
| Option | What it does | Practical use |
|---|---|---|
path |
Writes the trace to a file. | Set a path when you want to open or archive the artifact. |
categories |
Controls included trace categories. Prefix a category with - to exclude it. |
Use a focused category set when you know which events you need; avoid adding categories without a reason. |
screenshots |
Includes screenshots in the trace; defaults to false. |
Enable only when visual changes during the recording help explain the issue. |
bufferSize |
Sets the trace buffer size. | Increase it only if the recording needs more room and you understand the memory tradeoff. |
Puppeteer documents a default tracing buffer of 200 MB (200,000 KB) when the size is omitted or zero. If you omit path, tracing.stop() can return the trace as a Uint8Array. Only one trace can be active at a time per browser. See the TracingOptions reference and Tracing class reference.
const traceBytes = await page.tracing.stop();
// traceBytes is a Uint8Array when no path was supplied.
For most investigations, start with the default categories and no screenshots. Add configuration only to answer a specific question; a larger or more detailed trace can take more resources and be harder to inspect.
4. Open and read the trace in Chrome DevTools
- Open Chrome DevTools and select Performance.
- Use the option to load a saved profile and choose
trace.json. - Zoom into the interval where the delay occurred. Inspect the main-thread activity and relevant performance markers.
- Use the analysis views to move from a broad clue to specific work.
| View | Best question to ask |
|---|---|
| Call tree | Which top-level activity accounts for substantial work, and what did it trigger? |
| Bottom-up | Which activity consumed time directly across the selected interval? |
| Event log | In what order did the events occur? |
Compare Self Time and Total Time. Self Time is work done directly by an activity; Total Time includes its children. High Total Time with much lower Self Time points you toward descendants to inspect. High Self Time indicates the activity itself deserves attention. These are complementary ways to explore one recording, not separate traces. See the Chrome Performance features reference.
5. Turn a trace clue into a fix
- Pick one conspicuous activity in the slow interval and identify its parent and children.
- Use Call tree to understand the larger activity and Bottom-up to locate direct time sinks.
- Write a concrete hypothesis, such as “this interaction repeatedly recalculates the same layout” or “this handler performs too much synchronous work.” Treat it as a lead, not proof.
- Change one likely cause.
- Repeat the same action with the same conditions and compare the relevant interval and work.
A trace records activity during an instrumented capture. It does not, by itself, prove the underlying cause or establish representative real-user performance. Validate a finding with a controlled before-and-after capture and, where appropriate, separate user-facing performance measurements.
6. Common problems and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
Tracing already started or a second start fails |
A trace is already active in that browser. | Stop the active trace before starting another. Puppeteer supports only one active trace per browser. |
| The trace file is missing | The stop call was skipped after an exception, or the path is not where expected. | Use try/finally so tracing stops even when navigation or the interaction throws; use a clear path. |
| The recording does not include the slow interaction | Tracing started after the action or stopped too early. | Start immediately before the action and stop after the relevant delay has completed. |
| The capture hangs at navigation | The chosen waitUntil condition may not occur, for example on a page with continuing network activity. |
Choose a completion condition suited to the page, or wait for a specific relevant selector before stopping the trace. |
| The trace is too large or difficult to interpret | The recorded interval or included event categories are broader than needed. | Capture only the relevant interval; use categories selectively and leave screenshots off unless needed. |
| The trace seems slower than normal behavior | Recording adds instrumentation overhead. Advanced paint instrumentation can significantly hinder performance. | Use the trace to locate work, not as a clean speed benchmark. Avoid advanced paint instrumentation unless it answers a specific question. |
| The trace file contains unexpected sensitive details | Trace artifacts may include script content or source maps; protocol logs can also contain sensitive information. | Inspect and restrict access to artifacts before sharing. Avoid publishing traces from sessions that contain secrets or personal data. |
7. Performance, reliability, and cost considerations
- Keep captures narrow: starting immediately before the event reduces unrelated activity and makes analysis easier.
- Repeat under comparable conditions: browser state, cache state, network conditions, and test data can change what appears in a trace. Record these conditions with your notes.
- Mind trace memory: the documented default buffer is 200 MB. A longer or more detailed recording can require more resources; configure a larger buffer only when necessary.
- Separate diagnosis from benchmarking: instrumentation can affect execution time, so validate performance changes with an appropriate measurement method.
- Protect artifacts: traces and diagnostic logs can contain sensitive data. Chrome’s guidance on saving and sharing performance traces explains export considerations.
- Cost: Puppeteer tracing and DevTools do not imply a ScreenshotNeo charge; ScreenshotNeo is an optional hosted screenshot API, described below. Account for your own browser execution and storage costs if you run captures in hosted infrastructure.
8. Capture a visual reference for a slow page
A screenshot can help document the page state associated with a trace, but a screenshot alone does not explain why rendering or interaction took time. Capture the same URL and state alongside your trace when a visual reference will help a bug report or comparison.
Or skip the browser setup
Puppeteer tracing is the right route when you need a browser timeline to investigate work. If you also need a rendered page image without managing a browser capture service, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its one-call request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers say which page verdict and billing status applied. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Can I use Puppeteer tracing to measure Core Web Vitals?
A trace can help investigate browser work related to a performance issue, but a single instrumented trace is not a representative field measurement. Use a suitable measurement setup for reporting user-facing metrics.
Should I enable screenshots in every trace?
No. The option defaults to false. Enable it when visual progression helps explain the event being investigated.
Can I share a trace publicly?
Only after reviewing it for sensitive content. Trace exports can include script contents and source maps, and diagnostic protocol logs may contain sensitive information.
Does ScreenshotNeo replace Puppeteer tracing?
No. ScreenshotNeo returns page captures; Puppeteer tracing records browser activity for timeline diagnosis. Use the one that matches the artifact you need.


