Feature request: scroll restoration
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.9k
- Forks
- 190
- Avg merge
- 12h 50m
- Merged PRs (30d)
- 3
Description
This has been requested before: #132, (partially) #166, #189 - but the suggested code is an incomplete solution:
import { useEffect } from "react";
import { useLocation } from "wouter";
export default function ScrollToTop() {
const [ pathname ] = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
This will scroll to the top of the page on any navigation, including when the user presses the back / forward history buttons, and when the user navigates in ways that only affect part of the page (e.g. changing a tab component which records the current tab in the URL). It will also have a brief flicker due to using useEffect rather than useLayoutEffect. A more complete solution would only reset the scroll position when navigating to new pages, restoring previous positions when navigating forward / back, and allowing an override on links to disable scrolling to the top of the page.
React Router provides this as a separate component, but with some necessary integration with the core. That seems like a reasonable approach to follow here too, since it fits the philosophy of not bloating the core library (saving space for people who don't need the feature).
https://reactrouter.com/en/main/components/scroll-restoration
Their (MIT licensed) code shows that there are quite a few considerations here, so it seems beneficial to offer this in the library rather than having each user recreate the functionality for themselves. This will also make it possible to add integrations such as allowing specific links to bypass the scrolling behaviour.
Generally, their approach:
- takes over the native scroll behaviour (i.e. when the whole page actually switches) by setting
history.scrollRestoration(docs) - listens for the
pagehideevent to capture current scroll position before navigating to another page (also captures refreshing) (docs) - stores scroll locations in session storage (likely to avoid taking over the
stateparameter used in the history API) - provides a custom implementation of scrolling to anchor elements (likely due to disabling the browser's native handling; this might not be required, but worth finding out what the disadvantages are of allowing native scroll handling)
- gives links /
navigatethe ability to bypass auto-scrolling by setting a flag - updates the scroll position inside a
useLayoutEffectcall to avoid flickering thatuseEffectwould cause.
I think a minimal approach could:
- listen for
pagehideevents to capture scroll position - record this position in the history API for the current page (by calling
history.replaceState) - this can be put into thestateobject since currently it's always just set tonull, which avoids the need to use session storage [edit: actually this probably won't work because by the timepagehidefires, the history has (probably?) already updated, so using session storage might be a requirement] - set up a
useLayoutEffectwithuseLocation()[0]as a dep. Internally this can checkhistory.stateto see if it needs to scroll- it would be better if this could depend on the
history.stateobject directly - would it be possible to expose this fromuseLocationsomehow?
- it would be better if this could depend on the
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the useLocation integration and the linked React Router scroll-restoration implementation. Trace how navigation, history.state, pagehide, and link or navigate options are exposed in wouter. Done means a focused scroll-restoration feature supports new-page resets, back/forward restoration, anchor handling, and an opt-out without bloating the core.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100