Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Jan 18, 2026

Summary by CodeRabbit

  • New Features

    • Per-navigation scroll restoration: specify scroll behavior for individual navigations, overriding the global router setting.
    • Persistence across history: per-navigation setting is stored so back/forward navigation preserves the behavior.
    • Navigation-level override takes priority over global configuration when present.
  • Tests

    • Added extensive end-to-end tests verifying scroll behavior (including smooth) across navigation paths and imperative navigations.
    • Added test IDs to links/buttons to support the new tests.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 18, 2026

📝 Walkthrough

Walkthrough

Adds an optional per-navigation scrollRestorationBehavior?: ScrollBehavior, threads it through navigation/build/commit flows into history state, and makes scroll-restoration resolution prefer the per-navigation override over the router default.

Changes

Cohort / File(s) Summary
Interface additions
packages/router-core/src/RouterProvider.ts, packages/router-core/src/link.ts
Added optional scrollRestorationBehavior?: ScrollBehavior to CommitLocationOptions and NavigateOptionProps.
Router flow updates
packages/router-core/src/router.ts
Threaded scrollRestorationBehavior through BuildNextOptions/buildAndCommitLocation into commitLocation; persisted value into history state as __TSR_scrollRestorationBehavior.
Scroll restoration resolution
packages/router-core/src/scroll-restoration.ts
Resolver now prefers per-navigation __TSR_scrollRestorationBehavior from location state, falling back to router.options.scrollRestorationBehavior.
History state shape
packages/history/src/index.ts
Added __TSR_scrollRestorationBehavior?: ScrollBehavior to ParsedHistoryState.
E2E app UI/test hooks
e2e/*/scroll-restoration-sandbox-vite/src/routes/__root.tsx, e2e/*/scroll-restoration-sandbox-vite/src/routes/index.tsx
Added data-testid attributes, imported/used useNavigate, and added UI controls (links/buttons) to exercise scrollRestorationBehavior.
E2E tests
e2e/*/scroll-restoration-sandbox-vite/tests/app.spec.ts
Replaced some selectors with test-id lookups and added extensive tests that monkey-patch window.scrollTo to assert behavior: 'smooth' when per-navigation override is used and absence otherwise.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client
participant Router
participant History
participant ScrollRestorer

Client->>Router: navigate({ ..., scrollRestorationBehavior })
Router->>Router: buildAndCommitLocation(..., scrollRestorationBehavior)
Router->>History: commitLocation(location, state + __TSR_scrollRestorationBehavior)
Note right of History: History stores state with __TSR_scrollRestorationBehavior
Client->>Router: browser back/forward triggers location change
Router->>ScrollRestorer: restoreScroll(location)
ScrollRestorer->>History: read location.state.__TSR_scrollRestorationBehavior
ScrollRestorer->>Client: call window.scrollTo(..., behavior: resolvedBehavior)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • nlynzaad
  • birkskyum

Poem

🐰 I hopped through code with gentle cheer,
A tiny flag now travels near,
Threaded in state from start to end,
So scrolls may glide where devs intend,
A rabbit's tweak — succinct and clear. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: adding per-navigation scrollRestorationBehavior functionality across the router packages and e2e tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

@nx-cloud
Copy link

nx-cloud bot commented Jan 18, 2026

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit 368c49e

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ❌ Failed 1m 13s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-20 01:18:39 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 18, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@6415

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@6415

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@6415

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@6415

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@6415

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@6415

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@6415

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@6415

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@6415

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@6415

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@6415

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@6415

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@6415

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@6415

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@6415

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@6415

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@6415

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@6415

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@6415

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@6415

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@6415

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@6415

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@6415

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@6415

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@6415

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@6415

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@6415

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@6415

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@6415

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@6415

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@6415

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@6415

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@6415

@tanstack/vue-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@6415

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-devtools@6415

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-ssr-query@6415

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6415

@tanstack/vue-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-client@6415

@tanstack/vue-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-server@6415

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@6415

commit: 368c49e

@schiller-manuel
Copy link
Contributor Author

needs #6398 to be merged first

@schiller-manuel schiller-manuel force-pushed the feat/scrollRestorationBehavior-per-navigation branch from 7beceed to 9f549da Compare January 19, 2026 23:07
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@e2e/solid-router/scroll-restoration-sandbox-vite/src/routes/index.tsx`:
- Around line 1-2: There are duplicate imports from '@tanstack/solid-router'
(createFileRoute and separately Link, linkOptions, useNavigate); consolidate
them into a single import statement by combining createFileRoute, Link,
linkOptions, and useNavigate into one import from '@tanstack/solid-router'
(update the import that currently declares createFileRoute and the other import
that declares Link, linkOptions, useNavigate to a single merged import).

In `@e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/index.tsx`:
- Around line 1-2: The file imports from '@tanstack/vue-router' twice;
consolidate them into a single import that includes createFileRoute, Link,
linkOptions, and useNavigate together by replacing the two separate import
statements with one combined import listing those symbols so all uses of
createFileRoute, Link, linkOptions, and useNavigate remain unchanged.
🧹 Nitpick comments (3)
e2e/react-router/scroll-restoration-sandbox-vite/src/routes/index.tsx (1)

1-3: Consolidate duplicate imports from @tanstack/react-router.

ESLint correctly flags that the module is imported twice. Merge the imports into a single statement.

Suggested fix
-import { createFileRoute } from '@tanstack/react-router'
 import * as React from 'react'
-import { Link, linkOptions, useNavigate } from '@tanstack/react-router'
+import {
+  createFileRoute,
+  Link,
+  linkOptions,
+  useNavigate,
+} from '@tanstack/react-router'
e2e/react-router/scroll-restoration-sandbox-vite/tests/app.spec.ts (1)

71-78: Consider extracting the scrollTo interception into a reusable helper.

This same interception pattern is repeated four times across the test file. While acceptable for e2e tests, extracting it would reduce duplication.

Regarding the ESLint any[] warnings: these occur inside addInitScript which runs in the browser context where TypeScript types don't apply at runtime, so the impact is minimal.

Optional: Extract helper function
async function setupScrollToInterception(page: Page) {
  await page.addInitScript(() => {
    ;(window as any).__scrollToCalls = []
    const originalScrollTo = window.scrollTo.bind(window)
    window.scrollTo = ((...args: Array<any>) => {
      ;(window as any).__scrollToCalls.push(args)
      return originalScrollTo(...args)
    }) as typeof window.scrollTo
  })
}

async function clearScrollToCalls(page: Page) {
  await page.evaluate(() => {
    ;(window as any).__scrollToCalls = []
  })
}

async function getScrollToCalls(page: Page): Promise<Array<any>> {
  return page.evaluate(() => (window as any).__scrollToCalls)
}
e2e/vue-router/scroll-restoration-sandbox-vite/tests/app.spec.ts (1)

57-64: Consider extracting the scrollTo interception setup.

The window.scrollTo interception pattern is duplicated across four tests. You could extract this to a helper function or use a Playwright fixture to reduce duplication.

Additionally, ESLint prefers Array<any> over any[] per project rules, though this is minor in test context.

Example helper extraction
async function interceptScrollTo(page: Page) {
  await page.addInitScript(() => {
    ;(window as any).__scrollToCalls = []
    const originalScrollTo = window.scrollTo.bind(window)
    window.scrollTo = ((...args: Array<any>) => {
      ;(window as any).__scrollToCalls.push(args)
      return originalScrollTo(...args)
    }) as typeof window.scrollTo
  })
}

async function getScrollToCalls(page: Page): Promise<Array<any>> {
  return page.evaluate(() => (window as any).__scrollToCalls)
}

async function clearScrollToCalls(page: Page) {
  await page.evaluate(() => {
    ;(window as any).__scrollToCalls = []
  })
}

Also applies to: 103-110, 167-174, 213-220

Comment on lines 1 to +2
import { createFileRoute } from '@tanstack/solid-router'
import { Link, linkOptions } from '@tanstack/solid-router'
import { Link, linkOptions, useNavigate } from '@tanstack/solid-router'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Consolidate duplicate imports to satisfy linting.

ESLint import/no-duplicates will fail with two imports from the same module. Merge them into a single import.

🛠️ Proposed fix
-import { createFileRoute } from '@tanstack/solid-router'
-import { Link, linkOptions, useNavigate } from '@tanstack/solid-router'
+import { createFileRoute, Link, linkOptions, useNavigate } from '@tanstack/solid-router'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { createFileRoute } from '@tanstack/solid-router'
import { Link, linkOptions } from '@tanstack/solid-router'
import { Link, linkOptions, useNavigate } from '@tanstack/solid-router'
import { createFileRoute, Link, linkOptions, useNavigate } from '@tanstack/solid-router'
🧰 Tools
🪛 ESLint

[error] 1-1: '@tanstack/solid-router' imported multiple times.

(import/no-duplicates)


[error] 2-2: '@tanstack/solid-router' imported multiple times.

(import/no-duplicates)

🤖 Prompt for AI Agents
In `@e2e/solid-router/scroll-restoration-sandbox-vite/src/routes/index.tsx` around
lines 1 - 2, There are duplicate imports from '@tanstack/solid-router'
(createFileRoute and separately Link, linkOptions, useNavigate); consolidate
them into a single import statement by combining createFileRoute, Link,
linkOptions, and useNavigate into one import from '@tanstack/solid-router'
(update the import that currently declares createFileRoute and the other import
that declares Link, linkOptions, useNavigate to a single merged import).

Comment on lines 1 to +2
import { createFileRoute } from '@tanstack/vue-router'
import { Link, linkOptions } from '@tanstack/vue-router'
import { Link, linkOptions, useNavigate } from '@tanstack/vue-router'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Consolidate duplicate imports from the same module.

ESLint correctly flags that @tanstack/vue-router is imported twice. Merge these into a single import statement.

Proposed fix
-import { createFileRoute } from '@tanstack/vue-router'
-import { Link, linkOptions, useNavigate } from '@tanstack/vue-router'
+import {
+  createFileRoute,
+  Link,
+  linkOptions,
+  useNavigate,
+} from '@tanstack/vue-router'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { createFileRoute } from '@tanstack/vue-router'
import { Link, linkOptions } from '@tanstack/vue-router'
import { Link, linkOptions, useNavigate } from '@tanstack/vue-router'
import {
createFileRoute,
Link,
linkOptions,
useNavigate,
} from '@tanstack/vue-router'
🧰 Tools
🪛 ESLint

[error] 1-1: '@tanstack/vue-router' imported multiple times.

(import/no-duplicates)


[error] 2-2: '@tanstack/vue-router' imported multiple times.

(import/no-duplicates)

🤖 Prompt for AI Agents
In `@e2e/vue-router/scroll-restoration-sandbox-vite/src/routes/index.tsx` around
lines 1 - 2, The file imports from '@tanstack/vue-router' twice; consolidate
them into a single import that includes createFileRoute, Link, linkOptions, and
useNavigate together by replacing the two separate import statements with one
combined import listing those symbols so all uses of createFileRoute, Link,
linkOptions, and useNavigate remain unchanged.

Copy link
Member

@SeanCassiere SeanCassiere left a comment

Choose a reason for hiding this comment

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

Will need to confirm, but I think you'll need to make sure this prop doesn't get printed to the DOM.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants