react-router scroll to top on every transition

I have an issue when navigating into another page, its position will remain like the page before. So it won't scroll to top automatically. I've also tried to use window.scrollTo(0, 0) on onChange router. I've also used scrollBehavior to fix this issue but it didn't work. Any suggestions about this?


Solution 1:

but classes are so 2018

ScrollToTop implementation with React Hooks

ScrollToTop.js

import { useEffect } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return (null);
}

export default withRouter(ScrollToTop);

Usage:

<Router>
  <Fragment>
    <ScrollToTop />
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </Fragment>
</Router>

ScrollToTop can also be implemented as a wrapper component:

ScrollToTop.js

import React, { useEffect, Fragment } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history, children }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return <Fragment>{children}</Fragment>;
}

export default withRouter(ScrollToTop);

Usage:

<Router>
  <ScrollToTop>
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </ScrollToTop>
</Router>

Solution 2:

This answer is only for v4 and not later versions.

The documentation for React Router v4 contains code samples for scroll restoration. Here is their first code sample, which serves as a site-wide solution for “scroll to the top” when a page is navigated to:

class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    return this.props.children
  }
}

export default withRouter(ScrollToTop)

Then render it at the top of your app, but below Router:

const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)

// or just render it bare anywhere you want, but just one :)
<ScrollToTop/>

^ copied directly from the documentation

Obviously this works for most cases, but there is more on how to deal with tabbed interfaces and why a generic solution hasn't been implemented.

Solution 3:

React 16.8+

If you are running React 16.8+ this is straightforward to handle with a component that will scroll the window up on every navigation:
Here is in scrollToTop.js component

import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export default function ScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
}

Then render it at the top of your app, but below Router
Here is in app.js

import ScrollToTop from "./scrollToTop";

function App() {
  return (
    <Router>
      <ScrollToTop />
      <App />
    </Router>
  );
}

Or in index.js

import ScrollToTop from "./scrollToTop";

ReactDOM.render(
    <BrowserRouter>
        <ScrollToTop />
        <App />
    </BrowserRouter>
    document.getElementById("root")
);