Skip to content

fix(toc): Track hash in state to fix browser back navigation scrolling#18628

Draft
jaffrepaul wants to merge 7 commits into
masterfrom
cursor/fix-browser-back-anchor-scroll-de6b
Draft

fix(toc): Track hash in state to fix browser back navigation scrolling#18628
jaffrepaul wants to merge 7 commits into
masterfrom
cursor/fix-browser-back-anchor-scroll-de6b

Conversation

@jaffrepaul

@jaffrepaul jaffrepaul commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

DESCRIBE YOUR PR

This PR fixes a browser navigation bug where clicking the back button would restore the URL hash but not scroll to the correct anchor position on the page.

Root Cause:
Commit 8a810dae3 (May 18, 2026) by Shannon Anahata added a hasScrolledToHash ref to fix anchor links opened in new tabs landing on the wrong section. While that fix worked for its intended purpose, the ref was never reset during the page lifecycle, causing issues with browser back/forward navigation. When users clicked back to return to a URL with a hash anchor, the browser correctly updated the URL but the page didn't scroll because the component thought it had already handled that scroll.

The Solution:
Instead of using refs and manually managing scroll flags, the fix uses React state to track the current hash. This leverages React's dependency system - when the hash changes (via browser back/forward or any other navigation), the effect automatically re-runs and scrolls to the correct section.

Additional Fixes from BugBot Analysis:

  1. Redundant scroll on TOC clicks: BugBot correctly identified that clicking TOC links causes the browser to scroll, then our effect scrolls again. We accept this tradeoff because:

    • Performance impact is negligible (one RAF'd scrollIntoView() call)
    • Alternative (only scrolling on popstate) broke browser back/forward navigation
    • Effect is essential for initial page load and back/forward correction
  2. Unwanted scrolls during re-renders: BugBot identified that parent re-renders creating new ignoreIds array references would trigger TOC rebuilds, causing users to be yanked back to hash anchors while reading. Fixed by:

    • Tracking which hash we've scrolled to via a ref
    • Skipping scroll when treeItems rebuild but hash hasn't changed
    • Resetting ref on hash changes to allow navigation/back/forward scrolls

Testing:
Manually tested:

  1. Navigate to /product/ai-in-sentry/seer/autofix/#handoff-to-coding-agents ✅ Scrolls correctly
  2. Click "Cursor Cloud Agent" link ✅ Navigates to new page
  3. Click browser back button ✅ Correctly scrolls back to the anchor section
  4. Click TOC links ✅ Smooth scrolling with no jank
  5. Scroll away from hash anchor ✅ Page stays put, doesn't jump back
  6. Trigger re-renders ✅ Still doesn't jump back to hash

After clicking back button - correctly scrolled to anchor

IS YOUR CHANGE URGENT?

  • Urgent deadline (GA date, etc.):
  • Other deadline:
  • None: Not urgent, can wait up to 1 week+

PRE-MERGE CHECKLIST

  • Checked Vercel preview for correctness, including links
  • PR was reviewed and approved by any necessary SMEs (subject matter experts)
  • PR was reviewed and approved by a member of the Sentry docs team

To show artifacts inline, enable in settings.

Slack Thread

Open in Web Open in Cursor 

The TableOfContents component uses a ref to track whether it has scrolled
to a hash anchor, preventing duplicate scrolls after the TOC renders. However,
this flag was never reset, causing issues with browser back/forward navigation.

When clicking back to restore a URL with a hash anchor, the browser correctly
updated the URL but the page didn't scroll to the anchor section because the
component thought it had already handled that scroll.

Add a hashchange event listener that resets the scroll flag when the hash
changes, allowing the component to properly handle browser back/forward
navigation and other hash changes.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Paul Jaffre <jaffrepaul@gmail.com>
@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
develop-docs Ready Ready Preview, Comment Jul 2, 2026 6:35pm
sentry-docs Ready Ready Preview, Comment Jul 2, 2026 6:35pm

Request Review

cursoragent and others added 3 commits July 2, 2026 17:09
Refactor the hash scroll logic to use React state instead of refs and
event listeners. This is more idiomatic and maintainable:

- Replace hasScrolledToHash ref with currentHash state
- Remove lastHash ref tracking
- Let React's dependency system handle re-execution when hash changes
- Simplify the logic by eliminating manual flag management

The effect now automatically re-runs when either the hash or treeItems
change, making the behavior more predictable and easier to understand.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Paul Jaffre <jaffrepaul@gmail.com>
Add cleanup function to cancel pending requestAnimationFrame callbacks
when the effect re-runs. This prevents multiple scroll operations from
queuing up if users rapidly click through TOC links or navigate quickly.

The cleanup ensures only the latest scroll request executes, avoiding
potential jank from competing scroll operations.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Paul Jaffre <jaffrepaul@gmail.com>
Explicitly return undefined in early exit path to satisfy the
consistent-return ESLint rule. Both branches of the useEffect now
have explicit return statements.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Paul Jaffre <jaffrepaul@gmail.com>
@cursor cursor Bot changed the title fix(toc): Reset anchor scroll flag on hash change for browser back navigation fix(toc): Track hash in state to fix browser back navigation scrolling Jul 2, 2026
@jaffrepaul jaffrepaul marked this pull request as ready for review July 2, 2026 17:21
Comment thread src/components/tableOfContents.tsx
Add comment explaining why we accept a redundant scroll when clicking
TOC links. The Sentry bot flagged this as a potential performance issue,
but attempts to optimize it by only scrolling on popstate events broke
browser back/forward navigation.

The redundant scroll (browser native + our effect) has negligible
performance impact and ensures correct behavior for:
- Initial page loads with hash (corrects layout shift)
- Browser back/forward navigation (corrects scroll position)

The alternative (only scrolling on popstate) broke core functionality,
making this an acceptable tradeoff.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Paul Jaffre <jaffrepaul@gmail.com>
Comment thread src/components/tableOfContents.tsx
BugBot identified that parent re-renders can cause unwanted scrolls:
- Parent re-renders → new ignoreIds array reference
- TOC rebuild effect runs → treeItems state changes
- Scroll effect triggers → user yanked back to hash anchor

Fix: Track which hash we've scrolled to via ref. When treeItems rebuild
but hash hasn't changed, skip the scroll. Reset the ref on hash changes
to allow scrolling for navigation/back/forward.

This prevents users from being pulled back to anchors while reading,
while still supporting:
- Initial page load scroll correction
- Browser back/forward navigation
- TOC link clicks

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Paul Jaffre <jaffrepaul@gmail.com>
Comment thread src/components/tableOfContents.tsx

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6361092. Configure here.

Comment thread src/components/tableOfContents.tsx
Fix race condition identified by Bugbot: scrolledHashRef was set before
the RAF executed, so if a re-render cancelled the RAF, the ref would
incorrectly indicate the scroll happened when it didn't. This caused
scrolls to be silently skipped.

Move the ref assignment inside the RAF callback to ensure it's only
marked as scrolled when the scroll actually executes.

Fixes: High severity Bugbot issue (race condition)
Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Paul Jaffre <jaffrepaul@gmail.com>
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.

2 participants