React Router Dom: Scroll To Top on Route Change

Updated: March 3, 2023 By: A Goodman 17 comments

If you are using React Router Dom for navigation and routing in your React app then you will have to handle scroll restoration on your own because the library no longer ships default scroll management (it used to provide an out-of-the-box solution in the past).

By default, if you have a long content page and then you navigate to another long content page, you will stay scrolled down. This behavior doesn’t make lots of sense because not many people like to read text upside down. The example below will show you how to automatically scroll to the top of the page on each route change. We will use the useEffect hook and the useLocation hook to get the job done.

Note: This article was recently updated to work properly with React Router 6.x and later. However, the code snippet for React Router 5.x is still available at the end of this tutorial for your reference.

The Example

Preview

Our example project has 2 routes, “/” (LandingPage) and “/product” (ProductPage). Each page is very long and has a Link component so we can navigate to the other page.

The Steps

1. Create a new React project and Install react-router-dom:

npm install react-router-dom --save

2. Create a wrapper component that handles scroll restoration for you:

// src/ScrollToTop.jsx
import { useEffect } from "react";
import { useLocation } from "react-router";

const ScrollToTop = (props) => {
  const location = useLocation();
  useEffect(() => {
    window.scrollTo(0, 0);
  }, [location]);

  return <>{props.children}</>
};

export default ScrollToTop;

3. Create a file named LandingPage.jsx in your src folder and add the following:

// src/LandingPage.jsx
import { Link } from "react-router-dom";

const LandingPage = (props) => {
  return (
    <div style={{ margin: "auto", width: "80%" }}>
      <h1>Landing Page</h1>
      <div style={{ backgroundColor: "pink", height: 700 }}></div>
      <div style={{ backgroundColor: "orange", height: 700 }}></div>
      <div style={{ backgroundColor: "purple", height: 700 }}></div>
      <Link to="/product">
        <h2>Go To Product Page</h2>
      </Link>
    </div>
  );
};

export default LandingPage;

4. Similarly, create a file named ProductPage.jsx in the src folder and add the following code:

// src/ProductPage.jsx
import { Link } from "react-router-dom";

const ProductPage = (props) => {
  return (
    <div style={{ margin: "auto", width: "80%" }}>
      <h1>Product Page</h1>
      <div style={{ backgroundColor: "green", height: 700 }}></div>
      <div style={{ backgroundColor: "grey", height: 700 }}></div>
      <div style={{ backgroundColor: "blueviolet", height: 700 }}></div>
      <Link to="/">
        <h2>Go To Home Page</h2>
      </Link>
    </div>
  );
};

export default ProductPage;

5. Last but not least, remove all the default code in App.js.

If you’re using React Router 6 or newer, add this:

// App.js
// Kindacode.com
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";

import LandingPage from "./LandingPage";
import ProductPage from "./ProductPage";
import ScrollToTop from "./ScrollToTop";

const App = () => {
  return (
    <Router>
      <ScrollToTop>
        <Routes>
          <Route path="/" element={<LandingPage />} />
          <Route path="/product" element={<ProductPage />} />
        </Routes>
      </ScrollToTop>
    </Router>
  );
};

export default App;

Notice that the ScrollToTop component wraps the Routes component but stays inside the Router component. The order is important.

If you’re working with React Router 5.x, use this:

// App.js
// Kindacode.com
import {
  BrowserRouter as Router,
  Route,
  Switch,
} from "react-router-dom";

import LandingPage from "./LandingPage";
import ProductPage from "./ProductPage";
import ScrollToTop from "./ScrollToTop";

const App = () => {
  return (
    <Router>
      <ScrollToTop>
        <Switch>
          <Route path="/" exact>
            <LandingPage />
          </Route>

          <Route path="/product">
            <ProductPage />
          </Route>
        </Switch>
      </ScrollToTop>
    </Router>
  );
};

export default App;

Notice that the ScrollToTop component wraps the Switch component but stays inside the Router component.

Conclusion

We’ve built a simple single-page app to demonstrate how to programmatically scroll to the top of the page on every route change. If you’d like to learn more about modern React and frontend development, take a look at the following articles:

You can also check our React category page and React Native category page for the latest tutorials and examples.

Subscribe
Notify of
guest
17 Comments
Inline Feedbacks
View all comments
Rodrigo
Rodrigo
8 months ago

Great article bro, you’re so cool. Thanks.

Rahat
Rahat
9 months ago

Great post Its solve my problem

Eric
Eric
1 year ago

This is a great article. Thanks a lot. I have implemented this but I encounter one challenge though. I have links that navigate to a different route and to a particular section. eg using “/pathname#section” With this, it still scrolls to the top of that route. How can I implement… Read more »

Darko
Darko
1 year ago
Reply to  Eric

const splitLocation = pathname.split(“/”)

Then you can set the scrollToTop function to trigger only when splitLocation[1] changes….

Tiago
Tiago
1 year ago
Reply to  Eric

Hey Eric, I have the exact same struggle. Any help would be great!

ari pramana
ari pramana
1 year ago

thansk mate

Franklyn
Franklyn
1 year ago

Thanks a lot. This article was helpful

Ben
Ben
1 year ago

thanks this was very helpful

Cenco
Cenco
1 year ago

Thanks!

elive
elive
2 years ago

I really appreciate your help! Have a nice day!

James Shields
James Shields
2 years ago

Great bit of code. There is one minor issue that it also scrolls to the top when you use the back button. It would be fantastic if it could retain previous position on back button.

Saliya Bandara
Saliya Bandara
2 years ago
Reply to  James Shields

You can use useNavigationType hook. It returns “POP” for back or forward buttons. This will help to retain previous positions.
more info

elive
elive
2 years ago
Reply to  Saliya Bandara

Thanks for your additional information! It works on me. The following is my code. Thanks again! function ScrollToTop({ children }: { children: ReactChild | ReactChildren }) {   const location = useLocation();   const navigationType = useNavigationType();   useEffect(() => {     // scroll to top except when you… Read more »

Donymous1
Donymous1
1 year ago
Reply to  elive

For folks that are still on react-router v5 you can use useHistory instead useNavigationType.

const ScrollToTop = (props=> {
    const location = useLocation();
    const history = useHistory();

    useEffect(() => {
        return () => {
            if(history.action !== “POP”){
                window.scrollTo(00);
            }
        }        
    }, [location]);

    return <> {props.children} </>
}

abc
abc
2 years ago

Really Helpful Thank you

TechDan
TechDan
2 years ago

Nice, Thanks alot

Related Articles