Explain Codes LogoExplain Codes Logo

React-router scroll to top on every transition

javascript
react-router
scrolling
smooth-scroll
Anton ShumikhinbyAnton ShumikhinΒ·Oct 18, 2024
⚑TLDR

Here's an efficient way to implement scroll-to-top behavior with a custom ScrollToTop component in React Router:

import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; const ScrollToTop = () => { const { pathname } = useLocation(); useEffect(() => { // Like an elevator to the top floor 😎 window.scrollTo(0, 0); }, [pathname]); // Readies the "elevator" every time pathname changes return null; }; // Somewhere within your <Router /> component, just drop <ScrollToTop />

Simply nest the ScrollToTop component inside your router structure to reset scroll position every time the route changes. It seamlessly becomes part of your app, with minimal manipulation of existing route components.

Enhance user experience with smooth scrolling

While the initial code snippet provides you with the functionality, let's not forget about the end-user experience. Add a smooth scroll:

useEffect(() => { // Like a pleasant elevator music, smooth! window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }); }, [pathname]);

By adding behavior: 'smooth', scrolling gradually navigates to the top, creating a more polished user experience.

Boost response time with useLayoutEffect

In situations where your components might fetch dynamic content that changes the scroll height post-render, useLayoutEffect comes into play:

import { useLayoutEffect } from 'react'; // ... rest of the code useLayoutEffect(() => { // Flash! ⚑ Faster than you can say "scroll" window.scrollTo(0, 0); }, [pathname]);

useLayoutEffect ensures a "flash" scroll to top even before the screen update, voiding any potential flicker that useEffect might cause.

Tailoring for special cases and memory leak prevention

In tabbed interfaces or cases where a top scroll isn't needed, you can take control based on the location history. Also, it's crucial to prevent potential memory leaks by unsubscribing from the history:

useEffect(() => { const unlisten = history.listen(() => { // Being picky about when to scroll ain't a crime if (shouldScrollToTop(history.location)) { window.scrollTo(0, 0); } }); // Breakup is hard.. but necessary πŸ’” return () => { unlisten(); }; }, [history]);

Here, shouldScrollToTop is a custom function that decides whether scrolling to the top is necessary for a given route.

Class components: preserve smooth scrolling with componentDidUpdate

For those of you working with React's class components, worry not. Similar behavior can be implemented using componentDidUpdate method:

import { withRouter } from 'react-router'; class ScrollToTop extends React.Component { componentDidUpdate(prevProps) { if (this.props.location !== prevProps.location) { // Old school, but gold πŸ‘‘ window.scrollTo(0, 0); } } render() { return null; } } export default withRouter(ScrollToTop);

The async data dilemma: Use state to overcome!

Working with asynchronous data often leads to rendering a page before all data is loaded. To scroll to top post all data load, combine with useState and useEffect for a graceful handling:

useEffect(() => { if (dataLoaded) { // Like waiting for all passengers before the plane takes off✈️ window.scrollTo(0, 0); } }, [dataLoaded, pathname]);

Here, dataLoaded is a state variable indicating the completion of your data fetching.