Skip to content

fix(history): restore blocked pop navigation by delta - #7908

Open
erictheswift wants to merge 3 commits into
TanStack:mainfrom
erictheswift:fix/history-blocked-pop-rollback
Open

fix(history): restore blocked pop navigation by delta#7908
erictheswift wants to merge 3 commits into
TanStack:mainfrom
erictheswift:fix/history-blocked-pop-rollback

Conversation

@erictheswift

@erictheswift erictheswift commented Jul 26, 2026

Copy link
Copy Markdown

Problem

When a browser POP is blocked, createBrowserHistory always calls
window.history.go(1) to undo it. That only reverses a single-entry Back:

  • blocking Forward attempts to move farther forward instead of returning;
  • blocking go(-2) returns only one entry, leaving the browser URL and the
    router's current location on different history entries.

There are also cases where the POP has already happened, but its computed delta
is unusable:

  • restored or legacy entries can expose the same navigation index, producing
    delta === 0;
  • after a hard reload during a router migration, the active entry can already
    use TanStack metadata while an older history@4 { key, state } entry has no
    __TSR_index, producing delta === NaN.

Passing either value back to window.history.go(-delta) can reload the current
page instead of restoring the entry that was active before the POP.

Fix

Undo an observed POP with its inverse delta: window.history.go(-delta).
For Back this remains go(1), while Forward and multi-entry GO operations now
return to the exact entry from which navigation started.

When the delta is zero or otherwise not a safe integer, the original entry
cannot be recovered without guessing a direction. In that case, accept the
already-completed browser traversal, synchronize currentLocation, and notify
subscribers instead of passing an unusable delta to history.go.

This means a blocker cannot restore a POP whose displacement is unknowable; the
browser traversal stands. That is intentional and avoids both a destructive
reload and an arbitrary move to the wrong history entry.

Tests

Added browser-history regressions for:

  • a blocked Forward;
  • a blocked multi-entry go(-2);
  • a blocked POP with delta === 0, asserting that no history.go call occurs
    and subscribers observe the accepted location with action index 0;
  • a hybrid post-reload stack whose active entry has TanStack metadata and whose
    previous entry still has the history@4 { key, state } shape. The test
    explicitly observes delta === NaN, verifies that history.go is not called,
    and verifies that browser history, currentLocation, state, and subscribers
    all adopt the already-opened legacy entry.

Validated with:

CI=1 NX_DAEMON=false pnpm nx run @tanstack/history:test:unit --outputStyle=stream --skipRemoteCache
CI=1 NX_DAEMON=false pnpm nx run @tanstack/history:test:types --outputStyle=stream --skipRemoteCache
CI=1 NX_DAEMON=false pnpm nx run @tanstack/history:test:eslint --outputStyle=stream --skipRemoteCache
pnpm exec prettier --check packages/history/src/index.ts packages/history/tests/createBrowserHistory.test.ts

The Forward and multi-entry tests fail on main by restoring the wrong entry.
The zero-delta regression fails on the previous implementation by attempting
history.go(0). The hybrid-history regression independently pins the
non-safe-integer branch: removing only that guard leaves the zero-delta test
green while the legacy traversal fails.

Summary by CodeRabbit

  • Bug Fixes

    • Improved browser history rollback handling for blocked back/forward and multi-step navigations by rolling back the correct movement distance.
    • Avoids unsafe rollback when movement is zero or not a valid number, instead accepting the traversed state and still notifying subscribers with the correct location update.
    • Adds coverage for blocked pops into legacy-style history entries.
  • Tests

    • Expanded browser history tests with seeded multi-entry state and async location assertions.
    • Added scenarios for blocked navigations with zero movement and legacy entries, including verification of expected subscriber notifications and navigation behavior.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Blocked browser-history pop navigations now use the inverse computed delta for rollback, while zero or unsafe deltas are accepted without rollback. Tests cover forward, multi-entry, zero-delta, and legacy-entry blocked navigation.

Changes

Browser history rollback

Layer / File(s) Summary
Delta-aware blocked navigation handling
packages/history/src/index.ts
Blocked pop navigations roll back with history.go(-delta) when the delta is valid and nonzero; zero or unsafe deltas update the current location without rollback.
Blocked navigation restoration tests
packages/history/tests/createBrowserHistory.test.ts
Seeded TSR and legacy history entries validate blocked forward, multi-entry, zero-delta, and legacy-entry navigation, including subscriber notifications and avoided rollback calls.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: restoring blocked pop navigation using the observed delta.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant