Skip to content

resetScroll from a navigation is lost when the destination navigates while it is still settling #8028

Description

@daveycodez

Describe the bug

A navigation's resetScroll intent is stored in a single slot on the router rather than per navigation:

// packages/router-core/src/router.ts
this._scroll.next = next.resetScroll ?? true

If a second navigation is committed while the first is still settling, it overwrites that slot and the first navigation's scroll reset is silently dropped.

This shows up with an ordinary pattern: a page derives state from its own data and writes it back into its URL on first render.

useEffect(() => {
  if (color) return
  navigate({
    to: '/results',
    replace: true,
    resetScroll: false, // correct: this is not a page change
    search: (prev) => ({ ...prev, color: 'w,u,c' }),
  })
}, [color, navigate])

resetScroll: false is correct for that navigation — it only adds a search param to the route you are already on, and it should not move the viewport. But it lands in the same slot as the resetScroll: true recorded by the link navigation that is still settling. That navigation then never resets, and the user is left looking at the new page at the previous page's scroll offset.

Omitting resetScroll: false is not a workaround either: the reset then happens, but under the second navigation's timing, so the destination is painted at the old scroll offset first and visibly jumps to the top afterwards.

The two navigations have independent, explicitly documented resetScroll values, so neither should be able to consume or cancel the other's.

Your Example Website or App

https://github.com/daveycodez/tanstack-router-scroll-repro

Steps to Reproduce the Bug or Issue

  1. npm install && npm run dev
  2. Scroll to the bottom of the home page
  3. Click "open the results page"

Or run the included Playwright test: npx playwright install chromium && npm test

Expected behavior

Following the link resets scroll to the top of the results page.

Actual behavior

Scroll stays at the home page's offset (~8400px). The read-out in the bottom-right corner turns red.

The only variable is resetScroll: false on the second navigation, over 3 runs each:

second navigation link navigation's reset
resetScroll: false never applied — stays at 8420
default (true) applied — 0

Platform

  • @tanstack/react-router: 1.170.25
  • Browser: Chromium 141
  • OS: macOS

Additional context

Found while debugging a search page that infers filters from its results and writes them into the URL after render — the write cannot happen in beforeLoad/SSR there, because the filtered URL must not be the indexed one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions