13

How to scroll to top on route change with react router dom v6?

I have tried this, react-router scroll to top on every transition, which was my solution to make my page scroll to top on route change when I use react-router-dom v5. Now, I am using react-router-dom v6 and this solution does not work.

I tried React-router v6 window.scrollTo does not work and does not work for me.

I tried https://github.com/remix-run/react-router/issues/7365, which is to use the preload prop to trigger the scrollTo(0,0), also does not work for me.

jethro-dev
  • 249
  • 1
  • 5
  • 12
  • 1
    Scrolling to top didn't really change between versions, what is your code doing that isn't working? Please provide a [Minimal, Complete, and Reproducible Code Example](https://stackoverflow.com/help/minimal-reproducible-example). Also, `preload` isn't a prop in RRDv6. – Drew Reese Dec 02 '21 at 04:56
  • Unfortunately, Something changed. No matter what I do or what code I implement, top: 0,0 is never reached in react-router-dom when the new page is rendered. – Erik James Robles Mar 16 '22 at 05:31
  • https://stackoverflow.com/a/71753162/2902063 – alextrastero Apr 05 '22 at 14:15

6 Answers6

16

Well I'm not really sure what your layout looks like but inside your <BrowserRouter /> you can wrap your app in a wrapper and check for the location change in a useLayoutEffect. Then if there is a change you can scroll to the top. Here is a crude example.

Codesandbox

import { BrowserRouter, Routes, Route, Link, useLocation } from 'react-router-dom'
import { useLayoutEffect } from 'react'

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

const Component = ({title}) => {
  return (
    <div>
      <p style={{paddingTop: '150vh'}}>{title}</p>
    </div>
  )
}

const App = () => {
  return (
    <BrowserRouter>
      <Wrapper>
        <p>Scroll the bottom to change pages</p>

        <Routes>
          <Route path="/" element={<Component title="Home"/>} />
          <Route path="/about" element={<Component title="About"/>} />
          <Route path="/product" element={<Component title="Product"/>} />
        </Routes>

        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/product">Product</Link>
      </Wrapper>
    </BrowserRouter>
  )
}

export default App
Steve K
  • 8,505
  • 2
  • 20
  • 35
  • how about using `window.scrollTo(0, 0)` instead of `document.documentElement.scrollTo(0, 0)`? – AMATEUR_TOSS Jun 08 '22 at 06:47
  • 1
    @AMATEUR_TOSS Either way is fine.`window` just refers to the root and `document.documentElement` just refers to the element. Both will work and they are basically doing the same thing. You shouldn't run into any issues using either one when using `scrollTo()` I don't think. – Steve K Jun 08 '22 at 16:40
  • this does not work in 2022 July – Manasseh Codes Jul 21 '22 at 13:28
  • @ManassehCodes It works just fine. Since you didn't add any context to your comment I don't know what you are doing wrong. I added a link to a codesandbox in the answer so you can see it working. – Steve K Jul 21 '22 at 14:49
5

this article resolves this issue See it -> https://www.matthewhoelter.com/2022/04/02/how-to-scroll-to-top-on-route-change-with-react-router-dom-v6.html

make ScrollToTop component

and then add this code init

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

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

  useEffect(() => {
    // "document.documentElement.scrollTo" is the magic for React Router Dom v6
    document.documentElement.scrollTo({
      top: 0,
      left: 0,
      behavior: "instant", // Optional if you want to skip the scrolling animation
    });
  }, [pathname]);

  return null;
}

and then import it in your App.js and now your issue is resolved

see img

  • I used this solution and it works like a charm! NOTE: I had to import the `` on all the main components where I wanted to scroll to top on initial render. Even if you refresh it'll redirect to top of the screen. – Awshaf Ishtiaque May 24 '23 at 20:44
1

The window function cannot be accessed in newer versions of react, it is better to use the useRef Hook.

const myRef = useRef<any>(null);
const executeScroll = () => myRef.current.scrollIntoView({inline: "center"});

useEffect(() => {
    executeScroll();
  }, [location.pathname]);
  
// YOUR COMPONENTS CODE
// I have my toolbar as the ref
<Toolbar ref={myRef}/>

This will scroll when using the useNavigator and the pathname changes.

1

I use ScrollRestoration. I tried, if did't use ScrollRestoration component, window.scrollTo(0, 0) it didn't work.

 import { Outlet, useLocation, ScrollRestoration } from 'react-router-dom'
    function App() {
       const { pathname } = useLocation()
       useLayoutEffect(() => {
          window.scrollTo(0, 0)
       }, [pathname])
       return (
           <div className="App">
              <TopHeader />
              <Outlet />
              <Footer />
              <ScrollRestoration />
           </div>
        )
     }

     export default App
reborn
  • 11
  • 2
0
const scrollToPosition = (top = 0) => {
  try {
    /**
     * Latest API
     */
    window.scroll({
      top: top,
      left: 0,
      behavior: "smooth",
    });
  } catch (_) {
    /**
     * Fallback
     */
    window.scrollTo(0, top);
  }
};

You can use the above code to scroll top.

const didMount = useDidMount();
const router = useRouter();
const { asPath } = router;
useEffect(() => {
    if (didMount) {
      scrollToPosition();
    }
  }, [asPath]);

And add the above code to the top parent component.

0

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 returning to a previous page, use the ScrollRestoration component (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 the root component of your application. Here is an example with a functional component:

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