What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
For a single-page app (SPA), do not rely on Page.frameNavigated to detect every route change. That event represents a completed frame navigation associated with a new loader. A client-side transition caused by history.pushState(), history.replaceState(), back/forward navigation, or an anchor update is a same-document navigation. In the Chrome DevTools Protocol (CDP), listen for Page.navigatedWithinDocument and keep Page.frameNavigated for document-level navigations.
This guide shows how to subscribe to both events, identify the application’s main frame, interpret the payload, diagnose missed routes, and decide when browser-level instrumentation is the wrong tool.
Which CDP event detects an SPA URL change?
Use Page.navigatedWithinDocument. CDP fires it when navigation occurs without loading a new document, including History API use and fragment (anchor) navigation. Its payload identifies the frame, reports the new URL, and classifies the trigger.
Page.frameNavigated has a different contract: it fires after a frame navigation completes and the frame is associated with a new loader. A full navigation from /products to /pricing can produce it, but a router that calls history.pushState() normally does not create a new document or loader.
#1 Best Overall
| Question | Page.frameNavigated |
Page.navigatedWithinDocument |
|---|---|---|
| What it signals | A frame/document navigation completed and the frame received a new loader. | A same-document navigation occurred. |
| Useful for SPA route URL changes | Not by itself. | Yes. |
| Payload to inspect | Frame object and navigation context. | frameId, url, and navigationType. |
| Navigation types | Not the same-document classification. | fragment, historyApi, or other. |
| Main caution | An event from an iframe is not automatically an app-level route. | The event is marked experimental in the current Page-domain reference; verify support in your browser’s protocol version. |
How the two events relate
Full document navigation
When the browser loads a new document, listen for Page.frameNavigated. Examples include entering a new origin, following a normal link that replaces the document, or reloading the current page. The event contains a frame record; inspect that record rather than assuming the event belongs to the top-level page.
Same-document navigation
When the URL changes while the current document remains loaded, listen for Page.navigatedWithinDocument. The event’s navigationType tells you whether CDP classified the transition as a fragment navigation, a History API navigation, or other. Preserve other in your logs; it is a protocol value, not a reason to guess which router method ran.
Why subscribing to both is useful
Register both listeners before exercising the application. The resulting trace distinguishes a real document load from a client-side route update and exposes iframe activity that might otherwise look like a top-level navigation. This is especially helpful when an SPA occasionally performs hard navigations for authentication, downloads, or cross-origin transitions.
Node.js example with a CDP session
The following example uses Puppeteer only to launch Chromium and obtain a raw CDP session. The event names and payloads are CDP Page-domain events, so the same handling pattern applies when your project uses another CDP client.
Install and run
npm install puppeteer
Save this as track-navigation.js, then run node track-navigation.js. Replace the example URL with the application you need to observe.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
// executablePath: '/path/to/chrome' // set this when using a system browser
});
try {
const page = await browser.newPage();
const client = await page.target().createCDPSession();
await client.send('Page.enable');
client.on('Page.frameNavigated', event => {
const frame = event.frame || {};
console.log(JSON.stringify({
event: 'frameNavigated',
frameId: frame.id,
parentId: frame.parentId || null,
url: frame.url,
name: frame.name || null,
loaderId: frame.loaderId || null
}));
});
client.on('Page.navigatedWithinDocument', event => {
console.log(JSON.stringify({
event: 'navigatedWithinDocument',
frameId: event.frameId,
url: event.url,
navigationType: event.navigationType
}));
});
await page.goto('https://example.com', {
waitUntil: 'domcontentloaded',
timeout: 60000
});
// Replace this with the UI action that changes your SPA route.
// await page.click('[data-test="open-settings"]');
// await page.goBack();
await new Promise(resolve => setTimeout(resolve, 5000));
} finally {
await browser.close();
}
})();
Enable the Page domain before navigation or interaction. If you attach listeners after calling page.goto(), you can miss the initial frameNavigated event. For a route test, perform the click, form submission, keyboard action, or back/forward operation after both handlers are installed.
Rank #2
Filtering the event to your application’s main frame
Modern pages commonly contain iframes for payments, video, advertising, identity providers, or embedded tools. Every frame has its own identity. Store the top-level frame ID and compare incoming events with it.
const mainFrameId = (await client.send('Page.getFrameTree'))
.frameTree.frame.id;
function isMainFrame(frameId) {
return frameId === mainFrameId;
}
client.on('Page.navigatedWithinDocument', event => {
if (!isMainFrame(event.frameId)) return;
console.log({
url: event.url,
navigationType: event.navigationType
});
});
Obtain the frame tree after enabling the Page domain and before triggering the route change. If the application itself replaces or recreates the top-level target, refresh your stored frame identity when a new target is attached. For ordinary child-frame navigations, use the parent relationship in frameNavigated payloads to keep them separate from the app route.
Recommended Free Tools
Interpreting navigationType
historyApi
This indicates a transition associated with the History API, such as pushState, replaceState, or a browser back/forward action that changes the session history entry without loading a new document. It tells you how CDP classified the browser navigation; it does not guarantee that a particular framework hook or component has finished rendering.
fragment
This identifies a fragment change, such as moving from /docs#intro to /docs#api. A fragment transition can scroll the existing document and may be unrelated to the application router. Decide whether your analytics or test should treat it as a route before recording it.
other
Keep this value as reported. The protocol allows a same-document navigation that does not fit the two named categories. Do not infer that it was definitely a click, a router redirect, or a specific JavaScript method.
A reliable debugging workflow
- Connect to the intended target. Confirm that your CDP client is attached to the tab or page where the route changes occur, not an extension page, service worker, or unrelated tab.
- Enable the Page domain. Send
Page.enablebefore registering or exercising navigation. - Register both listeners. Log the frame ID, URL, and navigation type for within-document events; log the frame record and loader information for frame events.
- Capture the initial frame tree. Identify the top-level frame and ignore child-frame events unless they are part of your use case.
- Reproduce the transition. Use the application’s own UI, a test action, and browser back/forward controls. Test both a router transition and a full reload.
- Compare timestamps and URLs. A URL event proves browser-observed navigation, not that the new view’s data fetches or rendering have completed.
- Add application-level readiness checks when needed. Wait for a route-specific selector, network condition, or framework signal after receiving the CDP event.
Common failures and fixes
No event after history.pushState()
Cause: listening only for Page.frameNavigated.
Fix: enable and subscribe to Page.navigatedWithinDocument. A History API update normally keeps the same document and loader.
You receive events, but the URL belongs to an iframe
Cause: treating every frame event as the application route.
Fix: compare event.frameId with the top-level frame ID and inspect parent relationships. Track child frames separately when they matter.
The first navigation is missing
Cause: listeners were attached after navigation began.
Fix: create the CDP session, send Page.enable, and register handlers before goto or any user action.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →The event appears, but the page still shows the old view
Cause: CDP reported URL navigation before application rendering or data loading finished.
Fix: use the event as the navigation boundary, then wait for a route-specific DOM selector, application-ready signal, or other deterministic condition.
Rank #4
A client or generated type does not contain the event
Cause: protocol-version mismatch. Tip-of-tree CDP documentation changes frequently and does not promise backward compatibility; the event is experimental in the current Page-domain reference.
Fix: check the Chromium build actually running, inspect its protocol definition, and regenerate or upgrade the client types accordingly. Do not assume that support in one Chrome version exists in another browser or older build.
Nothing happens in an extension
Cause: a Chrome extension is using the wrong API surface. CDP Page events are not the same as extension webNavigation events.
Fix: for an extension, declare the webNavigation permission and use chrome.webNavigation.onHistoryStateUpdated for History API changes. Fragment changes are exposed through a separate extension event. This is an extension-specific alternative, not a replacement for CDP in an automation client.
Performance, reliability, and data handling
Keep handlers lightweight
Event callbacks run as navigation notifications arrive. Write compact structured records to a queue or logger instead of performing expensive DOM extraction, screenshots, or network requests directly inside the callback. Downstream processing can consume the URL and frame metadata without delaying your browser actions.
Deduplicate by frame and URL when appropriate
A route may be revisited, and a page can emit a full navigation followed by application activity. If your consumer needs unique route visits, define a key such as frame ID plus URL and timestamp window. Do not discard repeated events blindly when measuring back/forward behavior.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteDo not treat CDP as a framework router API
CDP reports browser-observed navigation state. It does not promise a particular React, Vue, Angular, or custom-router callback order, nor does it certify that route data has loaded. Combine the protocol event with application-level readiness checks when test correctness depends on rendered content.
Plan for protocol drift
Pin or regularly verify the Chromium version used in automation. Keep generated CDP types aligned with that version, and feature-detect or gracefully disable the within-document listener when an older protocol does not expose it. Log the browser version alongside navigation traces so failures can be correlated with upgrades.
Or skip the browser setup
If your actual goal is obtaining a clean image or PDF of a URL rather than observing navigation events, ScreenshotNeo provides a single screenshot API call. Its capture process accepts cookie or consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; each response reports the result with X-Page-Verdict and X-Billed headers. An MCP server also exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
cURL
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 request options. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsWhen to choose CDP, an extension API, or application instrumentation
- Choose CDP when an automation client controls Chromium and needs browser-level frame and URL events.
- Choose
chrome.webNavigationwhen you are implementing a Chrome extension and need extension lifecycle events with the required permission. - Choose application instrumentation when you need router-specific names, navigation guards, data-loader completion, or exact render timing. CDP can provide the browser boundary, but only the application knows when its view is ready.
FAQ
Does navigatedWithinDocument fire for a hash change?
Yes. Fragment navigation is one of the documented classifications, reported as navigationType: "fragment".
Is this behavior guaranteed in every browser?
No. These semantics describe Chromium’s DevTools Protocol. Other browsers may expose different debugging protocols, and CDP tip-of-tree definitions can change without backward-compatibility guarantees.
Can I use the event to know which component rendered?
No. The payload identifies the frame, URL, and navigation classification. Component identity and render completion require instrumentation inside the application.
Frequently Asked Questions
Can a route change occur without any URL change?
Yes. An application can replace visible content while keeping the same URL; neither navigation event is a reliable signal for that purely visual state change.
Should I record the old URL as well as the new URL?
Record the new URL from the event and maintain the previous value in your own per-frame state if your analytics or test report needs a before-and-after transition.
What should I do if a site changes its top-level frame?
Refresh your frame-tree mapping after the target creates a new top-level frame, then resume filtering against the current main-frame ID.
Quick Recap
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.

