mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 00:09:23 +02:00
[PR #782] [MERGED] fix: prevent URL/content mismatch on rapid Pages Router navigation #842
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#842
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/782
Author: @NathanDrake2406
Created: 4/4/2026
Status: ✅ Merged
Merged: 4/7/2026
Merged by: @james-elicx
Base:
main← Head:fix/pages-router-nav-race📝 Commits (10+)
dd8c624fix: prevent URL/content mismatch on rapid Pages Router navigationf778364chore: trigger CI832d6c7refactor: clarify routeChangeError/hard-nav contract and simplify catch blocks8a07411test: add signal-aware and stale-first navigation cancellation tests4aef8f2test: remove Router.events listeners in finally blocks1539d8efix: address review feedback — defer NEXT_DATA write, extract helper, add hard-nav fallback0ccdca5fix: avoid duplicate hard navigation fallbackd99c9fbfix: align popstate hard-nav handling83c3877chore: clarify navigation fallback invariants2b6b409chore: tighten navigation regression proof📊 Changes
2 files changed (+840 additions, -30 deletions)
View changed files
📝
packages/vinext/src/shims/router.ts(+170 -30)📝
tests/shims.test.ts(+670 -0)📄 Description
Summary
_navInProgressguard innavigateClient()with a navigation ID counter + AbortController, fixing a race condition where rapidpush()calls left the URL bar showing one page while the content showed anotherrouteChangeErrorwith.cancelled = trueinstead of silently completing, matching Next.js behavior__NEXT_DATA__) now emitrouteChangeErrorinstead of incorrectly emittingrouteChangeCompletebefore the hard redirectHow it works
Each
navigateClient()call increments a monotonic_navigationIdand aborts the previous fetch viaAbortController. After everyawaitboundary (fetch, res.text, dynamic import, React import, app import), the function checks whether it is still the active navigation. If a newer one has started, it throwsNavigationCancelledError. Callers catch this and emitrouteChangeErrorinstead ofrouteChangeComplete.This mirrors the
clc(component load cancel) pattern in Next.js's Pages Router (getCancelledHandlerinpackages/next/src/shared/lib/router/router.ts).Test plan
push()calls: last navigation wins,__NEXT_DATA__reflects the winning pagerouteChangeCompletedoes not fire for the superseded navigationrouteChangeErrorfires with.cancelled = truefor the superseded navigationrouteChangeError, notrouteChangeCompletereplace()also cancels a supersededpush()🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.