320

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?

Anandesh Sharma
  • 436
  • 6
  • 22
adrian hartanto
  • 3,235
  • 2
  • 11
  • 9

36 Answers36

277

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>
zurfyx
  • 31,043
  • 20
  • 111
  • 145
  • 26
    Best solution I have seen so far on the internet. I just a bit confused why react-router project does not add a property `scrollToTop` to ``. It seems to be very frequently used feature. – stanleyxu2005 Apr 09 '20 at 07:37
  • What would be an appropriate way to unit test this? – Matt Jul 30 '20 at 09:32
  • 5
    you should check `action !== 'POP'`. Listener accept action as 2th arg – Atombit Aug 24 '20 at 14:34
  • 6
    also, you can just use useHistory hook instead of withRouter HOC – Atombit Aug 24 '20 at 14:35
  • 2
    I needed to add `history` into the dependency list for useEffect – magician11 Dec 07 '20 at 21:43
  • 10
    It works but it not handling back behaviour. I means when go back to previous page, previous page also load from the the top of scroll. For example I have list of 100 items, and clicking on particular item it navigate to another page. Now, when I if I click on list item which is scrolled to the down on first page and go to another page, another page will load from scroll to top, but if I go back then first page also load from scroll to top. However it should be load where we left off I mean from the bottom of scroll. – Kishan Bharda Jan 23 '21 at 07:44
  • 10
    For TypeScript users, the correct type of `history` prop is `History` from `import { History } from "history";` – Owl Apr 24 '21 at 09:30
  • It works for going forward on the website, but while pressing back, the page shifts either a bit up or a bit down. Any solutions for that? – Jesswin Oct 23 '21 at 18:51
  • It's better to use the `useHistory` hook. HOC is unnecessary – ICW Jan 18 '22 at 19:25
  • This works but also disrupts user experience. It doesn't handle the back behavior, doesn't work with URL query params (`?name=jake`), doesn't work with URL fragments (`#heading-anchor`) etc. These are the cases where you shouldn't scroll to the top. Folks, please do understand the shortcomings of this before implementing this in your project. The [answer by @Saeed](https://stackoverflow.com/a/61602724/3049655) is better. – Spikatrix Dec 26 '22 at 06:23
231

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")
);
Saeed Rohani
  • 2,664
  • 1
  • 13
  • 12
166

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.

mtl
  • 7,529
  • 2
  • 20
  • 22
  • 5
    You should also reset keyboard focus at the same time as scrolling to top. I wrote a thing to take care of it: https://github.com/oaf-project/oaf-react-router – danielnixon May 09 '19 at 05:00
  • 4
    this extract from the docs `Because browsers are starting to handle the “default case” and apps have varying scrolling needs (like this website!), we don’t ship with default scroll management. This guide should help you implement whatever scrolling needs you have.` makes me sad, because all the options they go on to give code examples for, could actually be baked into the control via property settings, with some conventions around default behavior that can then be changed. This feels super hacky and unfinished, imho. Means only advanced react devs can do routing properly and no #pitOfSuccess – snowcode Sep 19 '19 at 09:15
  • 3
    oaf-react-router appears to address important accessibility issues that all the `work-arounds` here have ignored. +100 for @danielnixon – snowcode Sep 19 '19 at 09:28
  • ScrollToTop is not defined. How to import it in App.js? import ScrollToTop from './ScrollToTop' doesn't work. – anhtv13 Jul 22 '20 at 09:12
  • @anhtv13, you should ask that as a new question so you can include the code you are referencing. – mtl Jul 22 '20 at 14:04
  • My question is on your answer. It's not clear how to use the ScrollToTop. By the way, I found out the import on zurfyx's answer. – anhtv13 Jul 23 '20 at 02:10
  • 1
    you should check `action !== 'POP'`. Listener accept action as 2th arg – Atombit Aug 24 '20 at 14:35
  • This is a helpful answer but mentioning index.js as the location where App is rendered would improve it. Also for me the idea of "or just render it bare anywhere you want" does not work because of error about the component not having any children or some such. – biomiker Nov 05 '21 at 19:12
41

A React Hook you can add to your Route component. Using useLayoutEffect instead of custom listeners.

import React, { useLayoutEffect } from 'react';
import { Switch, Route, useLocation } from 'react-router-dom';

export default function Routes() {
  const location = useLocation();
  // Scroll to top if path changes
  useLayoutEffect(() => {
    window.scrollTo(0, 0);
  }, [location.pathname]);

  return (
      <Switch>
        <Route exact path="/">

        </Route>
      </Switch>
  );
}

Update: Updated to use useLayoutEffect instead of useEffect, for less visual jank. Roughly this translates to:

  • useEffect: render components -> paint to screen -> scroll to top (run effect)
  • useLayoutEffect: render components -> scroll to top (run effect) -> paint to screen

Depending on if you're loading data (think spinners) or if you have page transition animations, useEffect may work better for you.

Gabe M
  • 2,391
  • 1
  • 15
  • 8
40

This answer is for legacy code, for router v4+ check other answers

<Router onUpdate={() => window.scrollTo(0, 0)} history={createBrowserHistory()}>
  ...
</Router>

If it's not working, you should find the reason. Also inside componentDidMount

document.body.scrollTop = 0;
// or
window.scrollTo(0,0);

you could use:

componentDidUpdate() {
  window.scrollTo(0,0);
}

you could add some flag like "scrolled = false" and then in update:

componentDidUpdate() {
  if(this.scrolled === false){
    window.scrollTo(0,0);
    scrolled = true;
  }
}
Lukas Liesis
  • 24,652
  • 10
  • 111
  • 109
29

For react-router v4, here is a create-react-app that achieves the scroll restoration: http://router-scroll-top.surge.sh/.

To achieve this you can create decorate the Route component and leverage lifecycle methods:

import React, { Component } from 'react';
import { Route, withRouter } from 'react-router-dom';

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

  render() {
    const { component: Component, ...rest } = this.props;

    return <Route {...rest} render={props => (<Component {...props} />)} />;
  }
}

export default withRouter(ScrollToTopRoute);

On the componentDidUpdate we can check when the location pathname changes and match it to the path prop and, if those satisfied, restore the window scroll.

What is cool about this approach, is that we can have routes that restore scroll and routes that don't restore scroll.

Here is an App.js example of how you can use the above:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Lorem from 'react-lorem-component';
import ScrollToTopRoute from './ScrollToTopRoute';
import './App.css';

const Home = () => (
  <div className="App-page">
    <h2>Home</h2>
    <Lorem count={12} seed={12} />
  </div>
);

const About = () => (
  <div className="App-page">
    <h2>About</h2>
    <Lorem count={30} seed={4} />
  </div>
);

const AnotherPage = () => (
  <div className="App-page">
    <h2>This is just Another Page</h2>
    <Lorem count={12} seed={45} />
  </div>
);

class App extends Component {
  render() {
    return (
      <Router>
        <div className="App">
          <div className="App-header">
            <ul className="App-nav">
              <li><Link to="/">Home</Link></li>
              <li><Link to="/about">About</Link></li>
              <li><Link to="/another-page">Another Page</Link></li>
            </ul>
          </div>
          <Route exact path="/" component={Home} />
          <ScrollToTopRoute path="/about" component={About} />
          <ScrollToTopRoute path="/another-page" component={AnotherPage} />
        </div>
      </Router>
    );
  }
}

export default App;

From the code above, what is interesting to point out is that only when navigating to /about or /another-page the scroll to top action will be preformed. However when going on / no scroll restore will happen.

The whole codebase can be found here: https://github.com/rizedr/react-router-scroll-top

Yangshun Tay
  • 49,270
  • 33
  • 114
  • 141
Dragos Rizescu
  • 3,380
  • 5
  • 31
  • 42
  • 2
    Just what I was looking for! I had to add the scrollTo in the componentDidMount of the ScrollToTopRoute, as I have my routes wrapped in a Switch component (I also removed the if as I wanted it to fire on each mount) – tocallaghan Jun 20 '17 at 05:07
  • In the render of your ScrollToTopRoute, you don't need to specify the render={props => ()}. Simply using the {...rest} in the route will work. – Finickyflame Oct 20 '17 at 22:19
  • This answer provided me with all the tools to achieve a solution and fix my bug. Thank you so much. – Null isTrue Aug 01 '18 at 02:46
  • It is not working completely. If you follow the project link then it will be default open the `Home`. Now scroll to the bottom of the `Home` and now go to 'AnotherPage' and don't scroll. Now if you come again to `Home`, this page will be scrolled to the top. But according to your answer, the `home` page should be kept scrolled. – aditya81070 May 28 '19 at 18:41
  • } exact={true}/> Please how can i use the title attribute on the ScrollToTopRoute component ? – mhannani Dec 18 '20 at 00:14
18

It is noteable that the onUpdate={() => window.scrollTo(0, 0)} method is outdated.

Here is a simple solution for react-router 4+.

const history = createBrowserHistory()

history.listen(_ => {
    window.scrollTo(0, 0)  
})

<Router history={history}>
lehoang
  • 3,577
  • 1
  • 15
  • 16
  • This should be the correct answer if history is used. It covers all eventualites including clicking on a link to the page you're currently on. Only issue I found is the scrollTo didn't fire and needed to be wrapped in a setTimeout(window.scrollTo(0, 0),0); I still don't understand why but hey ho – sidonaldson May 30 '19 at 09:47
  • 5
    You will have a problems when you add `#` and `?` to the end of our url. In first case you should scroll no to the top in second case you shouldn't scroll at all – Arseniy-II Jul 24 '19 at 07:09
  • 4
    Unfortunately, this doesn't work with BrowserRouter. – VaibS Jun 04 '20 at 20:27
14

React hooks 2020 :)

import React, { useLayoutEffect } from 'react';
import { useLocation } from 'react-router-dom';

const ScrollToTop: React.FC = () => {
  const { pathname } = useLocation();
  useLayoutEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
};

export default ScrollToTop;
Kepro
  • 391
  • 6
  • 14
14

FOR 'REACT-ROUTER-DOM v6 & above'

I solved the following issue by creating a wrapper function and wrapping it around all the routes.

Follow the following steps:

1: You need to import the following:

import {Routes, Route, BrowserRouter as Router, useLocation} from 'react-router-dom';
import {useLayoutEffect} from 'react';

2: Write a wrapper function just above the "App" function:

const Wrapper = ({children}) => {
  const location = useLocation();
  useLayoutEffect(() => {
    document.documentElement.scrollTo(0, 0);
  }, [location.pathname]);
  return children
} 

3: Now wrap your routes within the wrapper function:

<BrowserRouter>
  <Wrapper>
    <Navbar />
      <Routes>
        <Route exact path="/" element={<Home/>} />
        <Route path="/Products" element={<Products/>} />
        <Route path="/Login" element={<Login/>} />
        <Route path="/Aggressive" element={<Aggressive/>} />
        <Route path="/Attendance" element={<Attendance/>} />
        <Route path="/Choking" element={<Choking/>} />
        <Route path="/EmptyCounter" element={<EmptyCounter/>} />
        <Route path="/FaceMask" element={<FaceMask/>} />
        <Route path="/Fainting" element={<Fainting/>} />
        <Route path="/Smoking" element={<Smoking/>} />
        <Route path="/SocialDistancing" element={<SocialDistancing/>} />
        <Route path="/Weapon" element={<Weapon/>} />
      </Routes>
    <Footer />
  </Wrapper>
</BrowserRouter>

This should solve the issue.

Hassan Shahzad
  • 455
  • 6
  • 14
11

I had the same issue with my application.Using the below code snippet helped me scroll to the top of the page on click of the next button.

<Router onUpdate={() => window.scrollTo(0, 0)} history= {browserHistory}>
...
</Router>

However, the issue still persisted on browser back. After a lot of trials, realized that this was because of the browser window's history object, which has a property scrollRestoration which was set to auto.Setting this to manual solved my problem.

function scrollToTop() {
    window.scrollTo(0, 0)
    if ('scrollRestoration' in history) {
        history.scrollRestoration = 'manual';
    }
}

<Router onUpdate= {scrollToTop} history={browserHistory}>
....
</Router>
Jhalaa Chinoy
  • 497
  • 5
  • 14
10

In your main component.

Just add this React Hooks (in case you are not using a React class) :

const oldPage = useRef(pathname)

useEffect(() => {
  if (pathname !== oldPage.current) {
    try {
      window.scroll({
        top: 0,
        left: 0,
        behavior: 'smooth'
      })
    } catch (error) {
      // for older browser
      window.scrollTo(0, 0)
    }
    oldPage.current = pathname
  }
}, [pathname])
Thomas Aumaitre
  • 651
  • 1
  • 7
  • 17
9

August-2021

Rather then doing it in every page you can do this in App.js

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

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

Setting location in useEffect will make sure to scroll to top on every path change.

rmlockerd
  • 3,776
  • 2
  • 15
  • 25
Azhar
  • 109
  • 1
  • 5
7

I want to share my solution for those who are using react-router-dom v5 since none of these v4 solutions did the work for me.

What solved my problem was installing react-router-scroll-top and put the wrapper in the <App /> like this:

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

and that's it! it worked!

Luis Febro
  • 1,733
  • 1
  • 16
  • 21
7

Hooks are composable, and since React Router v5.1 we have a useHistory() hook. So based off @zurfyx's answer I've created a re-usable hook for this functionality:

// useScrollTop.ts
import { useHistory } from 'react-router-dom';
import { useEffect } from 'react';

/*
 * Registers a history listener on mount which
 * scrolls to the top of the page on route change
 */
export const useScrollTop = () => {
    const history = useHistory();
    useEffect(() => {
        const unlisten = history.listen(() => {
            window.scrollTo(0, 0);
        });
        return unlisten;
    }, [history]);
};
Kris Dover
  • 544
  • 5
  • 9
  • It's worth nothing that scrollTo() doesn't work in IE11. `window.scrollTop = 0` is a good alternative – Turnip Dec 10 '20 at 05:19
  • 1
    @Nathan my understanding is in IE calling `scrollTo` on an Element doesn't work, but calling it on the `window` object does. See https://caniuse.com/mdn-api_window_scrollto vs https://caniuse.com/mdn-api_element_scrollto – Kris Dover Jan 10 '21 at 22:46
7

This was my approach based on what everyone else had done in previous posts. Wondering if this would be a good approach in 2020 using location as a dependency to prevent re-renders?

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

function ScrollToTop( { children } ) {
    let location = useLocation();

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

    return children
}
FreddieMixell
  • 300
  • 4
  • 15
7

2021 (React 16) - Based off the comments from @Atombit

Below scrolls to top, but also preserves historic scroll positions.

function ScrollToTop() {
  const history = useHistory()
  useEffect(() => {
    const unlisten = history.listen((location, action) => {
      if (action !== 'POP') {
        window.scrollTo(0, 0)
      }
    })
    return () => unlisten()
  }, [])
  return (null)
}

Usage:

<Router>
  <ScrollToTop />
  <Switch>
    <Route path="/" exact component={Home} />
  </Switch>
</Router>
Skatox
  • 4,237
  • 12
  • 42
  • 47
Ricky Boyce
  • 1,772
  • 21
  • 26
  • I was looking for this answer. However, my solution is to check that the action is "PUSH" so that a REPLACE does not trigger a scroll to top either. – FMCorz Sep 30 '21 at 11:17
6

My solution: a component that I'm using in my screens components (where I want a scroll to top).

import { useLayoutEffect } from 'react';

const ScrollToTop = () => {
    useLayoutEffect(() => {
        window.scrollTo(0, 0);
    }, []);

    return null;
};

export default ScrollToTop;

This preserves scroll position when going back. Using useEffect() was buggy for me, when going back the document would scroll to top and also had a blink effect when route was changed in an already scrolled document.

Sergiu
  • 345
  • 7
  • 7
5

With smooth scroll option

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

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

  useEffect(() => {
    window.scrollTo({
      top: 0,
      left: 0,
      behavior: 'smooth', 
    });
  }, [pathname]);

  return null;
}

...

<Router>
      <ScrollToTop />
     ...
    </Router>
atazmin
  • 4,757
  • 1
  • 32
  • 23
4

Utilizing hooks, you can simply insert window.scrollTo(0,0) in your useEffect in your code base. Simply implement the code snippet in your app and it should load each page at the top of it's window.

import { useEffect } from 'react';

useEffect(() => {
   window.scrollTo(0, 0);
}, []);
AT1990
  • 41
  • 2
4

2022 November Update

Nothing work in react latest version 18.2.0 and react-router-dom 6.4.3. So I implemented this. Worked for me.Hope this helpful for anyone.

ScrollToTop.js

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

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

  useEffect(() => {
    const body = document.querySelector('#root');
    body.scrollIntoView({
        behavior: 'smooth'
    }, 500)

}, [pathname]);

  return null;
}

Then import and add to browser router in index.js or App.js where your routes defined.

import { BrowserRouter, Routes, Route } from "react-router-dom";
import ScrollToTop from "./ScrollToTop";

function App() {
  return (
    <div>
      <BrowserRouter>
      <ScrollToTop />
        <Routes>
         //your routes
        </Routes>
      </BrowserRouter>
 </div>
  );
}

export default App;

(Note: Make sure the index.html div id="root".)

Dinith Rukshan Kumara
  • 638
  • 2
  • 10
  • 19
2

Since, I use function components, here is how I managed to achieve it.

import { useEffect } from 'react';
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';

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

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

    return null;
}

const IndexRoutes = () => {

    return (
        <BrowserRouter>
            <ScrollToTop />
            <Routes>
                <Route exact path="/">
                    <Home /> 
                </Route>
                /* list other routes below */
            </Routes>
        </BrowserRouter>
    );
};

export default IndexRoutes;

You can also refer the code from the below link

https://reactrouter.com/web/guides/scroll-restoration

Nishant Kohli
  • 445
  • 6
  • 6
2

For me, window.scrollTo(0, 0) and document.documentElement.scrollTo(0, 0) didn't work on all my screens (only worked on 1 screen).

Then, I realized that the overflow (where scrolling is allowed) of my screens were not in window (because we have some static points, so we putted the overflow: auto in other div).

I did the following test to realize this:

useEffect(() => {
  const unlisten = history.listen(() => {
    console.log(document.documentElement.scrollTop)
    console.log(window.scrollTop)
    window.scrollTo(0, 0);
  });
  return () => {
    unlisten();
  }
}, []);

In all the logs, I got 0.

So, I looked for which container I had the scroll in and put an id:

<div id="SOME-ID">
    ...
</div>

And in my ScrollToTop component I put:

useEffect(() => {
  const unlisten = history.listen(() => {
    window.scrollTo(0, 0);
    document.getElementById("SOME-ID")?.scrollTo(0, 0)
  });
  return () => {
    unlisten();
  }
}, []);

Now, when I go to a new route with history.push("/SOME-ROUTE") my screen go to the top

1

I wrote a Higher-Order Component called withScrollToTop. This HOC takes in two flags:

  • onComponentWillMount - Whether to scroll to top upon navigation (componentWillMount)
  • onComponentDidUpdate - Whether to scroll to top upon update (componentDidUpdate). This flag is necessary in cases where the component is not unmounted but a navigation event occurs, for example, from /users/1 to /users/2.

// @flow
import type { Location } from 'react-router-dom';
import type { ComponentType } from 'react';

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

type Props = {
  location: Location,
};

type Options = {
  onComponentWillMount?: boolean,
  onComponentDidUpdate?: boolean,
};

const defaultOptions: Options = {
  onComponentWillMount: true,
  onComponentDidUpdate: true,
};

function scrollToTop() {
  window.scrollTo(0, 0);
}

const withScrollToTop = (WrappedComponent: ComponentType, options: Options = defaultOptions) => {
  return class withScrollToTopComponent extends Component<Props> {
    props: Props;

    componentWillMount() {
      if (options.onComponentWillMount) {
        scrollToTop();
      }
    }

    componentDidUpdate(prevProps: Props) {
      if (options.onComponentDidUpdate &&
        this.props.location.pathname !== prevProps.location.pathname) {
        scrollToTop();
      }
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};

export default (WrappedComponent: ComponentType, options?: Options) => {
  return withRouter(withScrollToTop(WrappedComponent, options));
};

To use it:

import withScrollToTop from './withScrollToTop';

function MyComponent() { ... }

export default withScrollToTop(MyComponent);
Yangshun Tay
  • 49,270
  • 33
  • 114
  • 141
1

In your router.js, just add this function in the router object. This will do the job.

scrollBehavior() {
        document.getElementById('app').scrollIntoView();
    },

Like this,

**Routes.js**

import vue from 'blah!'
import Router from 'blah!'

let router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    scrollBehavior() {
        document.getElementById('app').scrollIntoView();
    },
    routes: [
            { url: "Solar System" },
            { url: "Milky Way" },
            { url: "Galaxy" },
    ]
});
Debu Shinobi
  • 2,057
  • 18
  • 21
1

If you want to not only have the scroll position reset to the top when navigating to a new page, but also to maintain the old scroll position when going to a previous page, use the ScrollRestoration component (available since React Router 6.4).

This component will emulate the browser's scroll restoration on location changes after loaders have completed to ensure the scroll position is restored to the right spot, even across domains.

Simply render it once in your root component:

import { ScrollRestoration } from 'react-router-dom';
function App() {
    return <>
        <div>Some content...</div>
        <ScrollRestoration/>
    </>;
}
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
0

Here is another method.

For react-router v4 you can also bind a listener to change in history event, in the following manner:

let firstMount = true;
const App = (props) => {
    if (typeof window != 'undefined') { //incase you have server-side rendering too             
        firstMount && props.history.listen((location, action) => {
            setImmediate(() => window.scrollTo(0, 0)); // ive explained why i used setImmediate below
        });
        firstMount = false;
    }

    return (
        <div>
            <MyHeader/>            
            <Switch>                            
                <Route path='/' exact={true} component={IndexPage} />
                <Route path='/other' component={OtherPage} />
                // ...
             </Switch>                        
            <MyFooter/>
        </div>
    );
}

//mounting app:
render((<BrowserRouter><Route component={App} /></BrowserRouter>), document.getElementById('root'));

The scroll level will be set to 0 without setImmediate() too if the route is changed by clicking on a link but if user presses back button on browser then it will not work as browser reset the scroll level manually to the previous level when the back button is pressed, so by using setImmediate() we cause our function to be executed after browser is finished resetting the scroll level thus giving us the desired effect.

Zeus
  • 1,235
  • 12
  • 20
0

with React router dom v4 you can use

create a scrollToTopComponent component like the one below

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)

or if you are using tabs use the something like the one below

class ScrollToTopOnMount extends Component {
    componentDidMount() {
      window.scrollTo(0, 0)
    }

    render() {
      return null
    }
}

class LongContent extends Component {
    render() {
      <div>
         <ScrollToTopOnMount/>
         <h1>Here is my long content page</h1>
      </div>
    }
}

// somewhere else
<Route path="/long-content" component={LongContent}/>

hope this helps for more on scroll restoration vist there docs hare react router dom scroll restoration

0

Using useEffect() - Solution for Functional Component

 useEffect(() => {
window.history.scrollRestoration = 'manual';}, []);
0

My only solution was to add a line of code to each file like for example:

import React from 'react';
const file = () => { document.body.scrollTop = 0; return( <div></div> ) }
Kassio
  • 19
  • 4
0

I used Typescript in my project. This worked for me:

  // ScrollToTop.tsx
import {useEffect, useRef} from 'react'
import {withRouter} from 'react-router-dom'

const ScrollToTopComponent = () => {
  const mounted = useRef(false)

  useEffect(() => {
    if (!mounted.current) {
      //componentDidMount
      mounted.current = true
    } else {
      //componentDidUpdate
      window.scrollTo(0, 0)
    }
  })

  return null
}

export const ScrollToTop = withRouter(ScrollToTopComponent)



// usage in App.tsx
export default function App() {
  return (
     <Router>
        <ScrollToTop />
        <OtherRoutes />
     </Router>
  )
}
Erik P_yan
  • 608
  • 5
  • 6
0

For my app, it's essential to have ability to scroll up when navigating by links. But it's also essential to not scroll anything when clicking on tab labels (which are also links). Also, my app has an advanced layout so scrollable containers could be different depending on the layout of the current page and where the link follows.

So here's my solution that works for me:

Introduce RouterLink wrapper around react-router-dom's Link that adds onClick handler. When link is clicked, it finds the nearest scrolled container and scrolls it up. It's possible to opt-out of this behavior by specifying preserveScroll (that's the default behavior of the original Link.

Hope that helps.

// Helper to find nearest scrolled parent of a given node
const getScrollParent = (node) => {
  if (!node) {
    return null;
  }

  if (node.scrollTop > 0) {
    return node;
  } else {
    return getScrollParent(node.parentNode);
  }
};

interface RouterLinkProps extends LinkProps {
  preserveScroll?: boolean;
}
export const RouterLink: React.FC<RouterLinkProps> = ({ preserveScroll = false, ...linkProps }) => {
  const handleClick = useCallback(
    (val) => {
      const targetEl = val?.target;
      if (!targetEl || preserveScroll) {
        return;
      }
      const scrolledContainer = getScrollParent(targetEl);
      if (scrolledContainer) {
        scrolledContainer.scrollTo({
          top: 0,
          left: 0,
          behavior: 'smooth',
        });
      }
    },
    [preserveScroll],
  );

  const extraProps = useMemo(() => {
    if (!preserveScroll) {
      return {
        onClick: handleClick,
      };
    }
    return {};
  }, [handleClick, preserveScroll]);

  return <Link {...linkProps} {...extraProps} />;
};

Now, I can use this wrapper and get desired behavior and enough control to adjust it. Like this:

<RouterLink to="/some/path">My Link that scrolls up</RouterLink>

Sergey P. aka azure
  • 3,993
  • 1
  • 29
  • 23
-1

I found that ReactDOM.findDomNode(this).scrollIntoView() is working. I placed it inside componentDidMount().

adrian hartanto
  • 3,235
  • 2
  • 11
  • 9
-1

For smaller apps, with 1-4 routes, you could try to hack it with redirect to the top DOM element with #id instead just a route. Then there is no need to wrap Routes in ScrollToTop or using lifecycle methods.

yakato
  • 1
  • 2
-1

for react-router-dom V5 react-router-dom V5 scroll to top

    import React, { useEffect } from 'react';
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link,
    useLocation,
    withRouter
} from 'react-router-dom'
function _ScrollToTop(props) {
    const { pathname } = useLocation();
    useEffect(() => {
        window.scrollTo(0, 0);
    }, [pathname]);
    return props.children
}
const ScrollToTop = withRouter(_ScrollToTop)
function App() {
    return (
        <div>
            <Router>
                <ScrollToTop>
                    <Header />
                     <Content />
                    <Footer />
                </ScrollToTop>
            </Router>
        </div>
    )
}
  • Generally it's helpful to put the contents from the document you've linked to in the answer itself, incase the destination changes or becomes unreachable. You can keep the link though, for reference! – brandonscript Oct 24 '21 at 17:50
  • @brandonscript it's updated with the content. i put the link so that content writer can get the full credit for taking the time to write the content and it will be helpful for others to read the whole content. but you right if the destination changes the putting the content will also be helpful – Surafel Getachew Oct 25 '21 at 12:19
-6
render() {
    window.scrollTo(0, 0)
    ...
}

Can be a simple solution in case the props are not changed and componentDidUpdate() not firing.

Eva Lond
  • 175
  • 1
  • 8
-11

This is hacky (but works): I just add

window.scrollTo(0,0);

to render();

J.J.
  • 350
  • 2
  • 6
  • 2
    so it will scroll up every time state changes and re-render happens, not when navigation changes. Just check my answer – Lukas Liesis Feb 27 '17 at 07:38