
How to test cookie consent across page navigation in Next.js
If consent works after a hard refresh but fails after an internal link, test the route transition itself. A Next.js application can preserve client state and loaded libraries while its visible page changes. The code that correctly waits on first load may not be the code that sends the next page-view event.
Start with two URLs: a simple entry page and a destination that loads a meaningful integration, such as a video or analytics event. Compare direct entry to the destination with navigation through the app. Do not rewrite the banner until you know which path produces the unexpected behavior.
Use this route test matrix
| Scenario | Starting state | Observation to record |
|---|---|---|
| Direct entry to /pricing | Fresh session; no choice | Initial script loads, storage writes and consent defaults |
| Internal link to /pricing | Reject on the entry page first | Whether a route event bypasses the saved refusal |
| Reload /pricing | Keep the saved refusal | Whether startup restores the same state |
| Accept analytics, then navigate | Analytics granted; other categories unchanged | Expected analytics behavior without granting unrelated categories |
| Withdraw, then navigate back | Previously accepted session | Whether an already loaded library continues sending |
| Open the destination in another tab | Persisted choice in the same profile | Whether the new tab restores the choice consistently |
These are test cases, not measured results for a particular Next.js website. Use real routes from your application and write the expected behavior before running them. If an embed is deliberately click-to-load, include the click as a separate action instead of expecting it to appear during an idle scan.
Separate script scheduling from permission to run
A script loading strategy does not represent a visitor's choice. Moving a tag from beforeInteractive to afterInteractive changes scheduling; it does not create a consent gate. Check the condition that permits initialization and the conditions that permit later event calls.
Next.js documents that onReady can run after loading and again when the component remounts. If your callback initializes tracking, inspect that remount path as well. See the Script component reference. Treat the callback as an entry point to review, not as a fix by itself.
Removing a script element after withdrawal does not undo code that already executed. A library may still have timers, listeners or queued events. Use its supported consent or shutdown behavior where available and verify the next event, not merely whether the script node disappeared from the DOM.
Follow one event through the application
- Record the production build identifier, route sequence and saved consent choice. Run the production build on staging when possible; development-only behavior can confuse the comparison.
- Keep the network capture across navigation. Note the moment of the route transition and the request you did not expect.
- Locate the route-change handler. In App Router code, inspect effects that depend on pathname or search parameters. In Pages Router code, inspect the router event integration actually used by the application.
- Check what consent value the handler reads at send time. A value captured earlier in a closure can differ from the current CMP state.
- Check whether the same event comes from both an application effect and a tag-manager history trigger. Disable one path on staging to isolate duplication.
- Repeat after withdrawal without reloading. Then perform a full reload and compare the two captures.
The usePathname documentation is a reference for observing pathname changes in Client Components. It does not supply consent state. Your CMP integration still needs a reliable way to expose the current choice to whichever code handles those changes.
Check unknown, denied and granted as separate states
An application often starts before its CMP has finished restoring a saved choice. Do not silently turn that unresolved state into granted. Define what may initialize while the state is unknown, what remains blocked when it is denied, and which category permits each integration. Keep the same rules for route events and delayed callbacks.
| Symptom | Candidate cause | Targeted retest |
|---|---|---|
| Only internal navigation sends the unwanted event | Route handler bypasses the consent gate | Reject, navigate through the exact route, inspect event timing |
| Two page views after one link click | Automatic tracking plus a manual handler | Keep only one event path on staging and repeat |
| Withdrawal works only after reload | Loaded SDK or stale callback still uses the old state | Withdraw and trigger another event without reloading |
| A returning visitor briefly gets tracking before refusal restores | Initialization races with CMP startup | Throttle the test connection and inspect event order |
| Only one nested route fails | Route-specific embed, layout or integration | Compare direct entry and client navigation to that route |
Each row gives a hypothesis. Verify it before changing production. For example, two requests can be different events rather than duplicates; compare their purpose and payload. Conversely, one visible cookie does not mean one integration is installed. Keep the investigation tied to the event you can reproduce.
Attach the route sequence to a developer-ready snapshot
Use Extension Pro to inspect captured requests and export a Debug Snapshot. Add your route steps and consent choices so the next developer can reproduce the same sequence.
Get Pro for consent debuggingWhat counts as a completed fix?
The previously failing transition must match the documented behavior with no choice, with refusal, with the relevant grant and after withdrawal. Check direct entry too. Fixing a client-side route event must not leave the server-rendered entry path loading the same integration unconditionally.
Write the result next to the build and configuration versions. Keep an explicit inconclusive state for captures where the CMP never loaded or the target request was blocked by the browser. For a reusable release checklist, use cookie consent regression testing; for a broader setup overview, see WordPress and Next.js consent implementation.
Will a scan of the destination URL catch a route-only problem?
Not necessarily. Loading a URL directly is a different test from reaching it through a client-side transition. Scheduled scans can help monitor selected entry pages, but keep an interaction test for the route sequence that previously failed. Do not describe a direct-page scan as full coverage of an SPA journey.
ConsentScope Team
Verified authorConsentScope product team
We build ConsentScope and write practical guides to inspecting browser storage, consent signals and network activity. Our examples distinguish recorded observations from test scenarios.
Related articles
WordPress and Next.js Cookie Consent: A Developer Test Guide
Find consent bugs in WordPress plugins and Next.js routes. Check duplicate tags, hydration, saved choices and revocation with a repeatable audit workflow.
Cookies still set after "Reject all"? How to find the cause
Separate old cookies from new writes, trace the request or script responsible, and retest rejection, reloads and consent withdrawal in your browser.
How to retest cookie consent after a website update
Retest consent after a release with a reusable scenario matrix. Compare clean visits, rejection, partial consent, withdrawal and navigation without misleading scores.