Add a scroll option to Navigation.navigate() and <Link>#975
Merged
Conversation
Aligns the default scroll behavior with React Router, Next.js, and
TanStack Router: every forward navigation (push or replace) resets the
scroll position to the top of the page, or to the URL's hash target.
Previously, replace navigations kept the page where it was.
Pass `scroll: false` (or `<Link scroll={false}>`) to keep the current
scroll position - useful when the URL encodes UI state (filters, tabs,
a selected calendar day) and the page should stay put. The kept offset
is recorded against the new history entry so a later back/forward
return still restores it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The URL change, not the history operation, now decides the default: push and replace both reset on a pathname/hash change and both stay put when only the search params change. Search-only navigations almost always encode UI state (filters, tabs, a selected day) rather than a new location. `scroll: true` / `scroll: false` force either behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
scrolloption toNavigation.navigate()and<Link>, and makes the URL change — not the history operation — decide the default scroll behavior.Default behavior change
For push and replace navigations alike:
Back/forward still restores each entry's saved offset, and reloads still restore the entry's offset.
This differs deliberately from React Router / Next.js / TanStack Router, which reset on search-only changes too (behind
preventScrollReset/scroll: false/resetScroll: falseopt-outs). Search-only navigations almost always encode UI state rather than a new location, so staying put is the default that matches intent; thescrolloption covers the exceptions in both directions.Previously, apps reached for
replaceto get its keep-scroll side effect. Now push/replace is purely a history-semantics choice, and scroll behavior follows the shape of the URL change.The
scrolloptionWith
scroll: false, the kept offset is recorded against the new history entry, so a later back/forward return to it still restores correctly. The option is a no-op when theNavigationwas constructed withscrollRestoration: false.Tests
New
packages/preact-router/source/Navigation.test.tscovers: manual-restoration setup,scrollRestoration: false, push/replace top reset on path changes, search-only keep (push and replace),scroll: trueforcing a reset,scroll: falsekeeping the position (push and replace), offset recording underscroll: false, hash-target scrolling (path change and same-path hash change), popstate restore, andsessionStoragepersistence.pnpm vitest run --config configuration/vite.unit.config.ts packages/preact-router/source/Navigation.test.ts— 14/14 pass.tsc --build packages/preact-router— clean.Note:
packages/preact-router/source/components/Link.test.tsxcurrently fails to transform onmainwith the pinned toolchain (rolldown can't parse esbuild's lowering ofusingdeclarations) — pre-existing and unrelated to this change.🤖 Generated with Claude Code