Add scroll restoration to the preact-router Navigation#939
Merged
Conversation
In a single-page app the browser's native scroll restoration is unreliable: on a back/forward navigation it restores against the document as it exists at popstate time, but an async route hasn't rendered yet, so it lands at the wrong offset; forward navigations are often left at the previous page's offset. Navigation now owns it. In the browser it sets history.scrollRestoration to 'manual' and keeps per-entry offsets keyed by navigation id, persisted to sessionStorage: forward resets to the top (or the URL hash target), back/forward restores the offset the entry was left at, and a reload restores its landing entry. Restores that need committed content (a saved offset or hash target) run on the next frame; a plain reset to top is synchronous to avoid a flash. Enabled by default; pass scrollRestoration:false to opt out. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.
Summary
Navigationnow manages scroll position across navigations, fixing thecommon SPA bug where client-side navigation lands at the wrong scroll offset.
The browser's native scroll restoration runs against the document as it exists
at
popstatetime, but an async route hasn't rendered its content yet — so aback/forward restore lands against the wrong (usually shorter) document. Forward
navigations are also frequently left at the previous page's offset rather than
the top.
In the browser,
Navigationnow:history.scrollRestoration = 'manual'and keeps its own per-entry scrolloffsets, keyed by navigation id and persisted to
sessionStorage(so theysurvive a reload within the tab session, capped at 50 entries);
when present;
Restores that need the destination route's content committed (a saved offset or
a hash target) are applied on the next animation frame; a plain reset to the top
is applied synchronously to avoid a flash of the new route at the previous
offset. All of this is window/document-scoped and fully guarded for non-browser
(SSR) construction.
Enabled by default. Pass
scrollRestoration: falseto leave scrolling to thebrowser/your app:
A changeset (
minorbump for@quilted/preact-router) is included.Test plan
Navigationscroll-restoration unit tests (jsdom): manual mode bydefault, opt-out, reset-to-top on forward nav, restore-on-back, and
sessionStorage persistence keyed by navigation id.
tsc --build packages/preact-router/tsconfig.jsonpasses.prettier --checkclean.pnpm testin CI (the equivalent suite was validated against the builtoutput in a downstream consumer; CI builds the workspace tooling first).