Skip to content

Add UI router scroll restoration - #609

Merged
kody-bot merged 4 commits into
mainfrom
cursor/ui-router-scroll-restoration-fb53
Jul 5, 2026
Merged

kody-bot merged 4 commits into
mainfrom
cursor/ui-router-scroll-restoration-fb53

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 5, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Adds a UI router scroll restoration primitive that stores per-history-entry scroll positions, restores on back/forward, scrolls hash targets after route render, and scrolls new navigations to top by default.
  • Adds preventScrollReset support for programmatic navigate(...) and data-prevent-scroll-reset for intercepted links/forms.
  • Extends E2E coverage for scroll reset, popstate restoration with distinct positions, hash-target scrolling, and per-link opt-out behavior.

Walkthrough

router-scroll-restoration-demo.mp4

Validation

  • npm run validate
  • npx playwright test e2e/ssr-hydration.spec.ts
  • npx vitest run --config vitest.node.config.ts packages/worker/client/router-scroll-state.node.test.ts packages/worker/client/client-router.node.test.ts
System recap — extends existing primitives (medium risk)

Mode: recap · Base: main @ 2a539a7 · Head: c84d04e

Classification: extends — this PR changes browser app routing behavior by adding scroll restoration semantics to SPA navigations.

Primitives touched

Primitive Group Impact
app-ui surfaces extends — router navigation now emits scroll metadata and the app shell mounts scroll restoration
community-listings assistant composes — E2E fixtures and a stable report anchor use community pages to verify router behavior

System map

flowchart LR
	appUi["app-ui"]:::extended
	communityListings["community-listings"]:::touched
	appUi --> communityListings
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Change flow

sequenceDiagram
	participant User
	participant Router as client-router
	participant Scroll as ScrollRestoration
	User->>Scroll: scroll current route
	Scroll->>Scroll: save current history entry position
	User->>Router: click link / navigate(to)
	Router->>Scroll: navigationstart saves outgoing non-POP position
	Router->>Router: run loader then commit history entry
	Router->>Scroll: navigationend with history action + preventScrollReset
	Scroll->>Scroll: wait a frame for destination DOM
	Scroll->>User: restore saved POP position, hash target, preserve, or top
Loading
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features
    • Added client-side scroll restoration for SPA navigation, including back/forward recovery and hash-based “jump to” scrolling.
    • Added an opt-out mechanism to prevent scroll reset via data-prevent-scroll-reset or preventScrollReset.
  • Bug Fixes
    • Improved consistency of scroll behavior across navigations and refreshes.
  • Documentation
    • Updated the client navigation flow documentation with scroll restoration rules and opt-out details.
  • Tests
    • Expanded SSR hydration end-to-end coverage to assert scroll restoration; added unit tests for scroll restoration utilities.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds scroll restoration for client-side navigation, including router event details and target selection, a ScrollRestoration component that persists positions, app-shell wiring, and e2e coverage for scroll behavior and seeded README content.

Changes

Scroll Restoration Feature

Layer / File(s) Summary
Scroll restoration types and utilities
packages/worker/client/router-scroll-state.ts, packages/worker/client/router-scroll-state.node.test.ts
New router history, navigation, and scroll-target types plus helpers for scroll-restoration keys, hash parsing, and target selection, with unit tests covering the cases.
Client router navigation details
packages/worker/client/client-router.tsx
Navigation clicks, forms, popstate handling, history state, and public navigate now carry scroll-restoration details and preventScrollReset through structured navigation events.
ScrollRestoration component and app wiring
packages/worker/client/scroll-restoration.tsx, packages/worker/client/app.tsx
A new component stores and restores scroll positions across router events and page lifecycle events, and the app mounts it in the client shell.
E2E coverage and docs
e2e/d1-utils.ts, e2e/ssr-hydration.spec.ts, docs/contributing/architecture/request-lifecycle.md
E2E seeding accepts listing README content, the SSR hydration spec exercises push/pop/hash scroll behavior, and the architecture doc describes the scroll-restoration flow.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • kentcdodds/kody#608: Extends the same client-router navigation lifecycle that this PR uses for scroll-restoration event details and history state.
🚥 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 summarizes the main change: adding router scroll restoration to the UI.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/ui-router-scroll-restoration-fb53

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.

Comment thread packages/worker/client/scroll-restoration.tsx
@github-actions

github-actions Bot commented Jul 5, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-609.kentcdodds.workers.dev

Worker: kody-pr-609
D1: kody-pr-609-db
KV: kody-pr-609-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
e2e/ssr-hydration.spec.ts (1)

61-114: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No coverage for hash-target scroll restoration.

The docs (docs/contributing/architecture/request-lifecycle.md, Lines 138-144) describe scrolling to hash targets as part of this feature, but this expanded flow only covers reset-to-top, restore-on-back, and prevent-scroll-reset. Consider adding an assertion for navigating to a URL with a hash to confirm the target scrolls into view, to fully cover the documented behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/ssr-hydration.spec.ts` around lines 61 - 114, The SSR hydration test in
ssr-hydration.spec.ts covers reset-to-top, back-navigation restore, and
prevent-scroll-reset, but it does not verify hash-target scrolling. Extend the
existing navigation flow around the community listing interactions to include a
URL with a hash and assert the page scrolls to the anchored element, using the
current page, scrollY checks, and the existing link/navigation helpers to
confirm the documented hash scroll behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/worker/client/scroll-restoration.tsx`:
- Around line 131-137: The scroll restoration flow in scroll-restoration.tsx is
saving the outgoing page’s position using the destination history state during
navigationstart, which overwrites the entry for the page being returned to.
Update saveWindowScrollPosition and the router event wiring so the current entry
is captured before the history move, or switch the persistence logic to scroll
plus pagehide, and keep restoreWindowScroll using the correct saved key.

---

Nitpick comments:
In `@e2e/ssr-hydration.spec.ts`:
- Around line 61-114: The SSR hydration test in ssr-hydration.spec.ts covers
reset-to-top, back-navigation restore, and prevent-scroll-reset, but it does not
verify hash-target scrolling. Extend the existing navigation flow around the
community listing interactions to include a URL with a hash and assert the page
scrolls to the anchored element, using the current page, scrollY checks, and the
existing link/navigation helpers to confirm the documented hash scroll behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5a8ce3b5-7e40-4be0-88c8-54c87b68bf2e

📥 Commits

Reviewing files that changed from the base of the PR and between 2a539a7 and bd80a28.

📒 Files selected for processing (8)
  • docs/contributing/architecture/request-lifecycle.md
  • e2e/d1-utils.ts
  • e2e/ssr-hydration.spec.ts
  • packages/worker/client/app.tsx
  • packages/worker/client/client-router.tsx
  • packages/worker/client/router-scroll-state.node.test.ts
  • packages/worker/client/router-scroll-state.ts
  • packages/worker/client/scroll-restoration.tsx

Comment thread packages/worker/client/scroll-restoration.tsx
Comment thread packages/worker/client/scroll-restoration.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.

There are 2 total unresolved issues (including 1 from previous review).

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 a7286ed. Configure here.

Comment thread packages/worker/client/client-router.tsx Outdated
@kody-bot
kody-bot merged commit d3bdb9e into main Jul 5, 2026
5 checks passed
@kody-bot
kody-bot deleted the cursor/ui-router-scroll-restoration-fb53 branch July 5, 2026 06:48
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.

3 participants