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

10

19

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.

Babs answered 2/12, 2021 at 2:59 Comment(3)
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. Also, preload isn't a prop in RRDv6.Herwig
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.Equestrienne
https://mcmap.net/q/665961/-scroll-to-top-on-every-transition-react-router-dom-v6-duplicateMorion
M
20

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
Mulligatawny answered 2/12, 2021 at 3:56 Comment(6)
how about using window.scrollTo(0, 0) instead of document.documentElement.scrollTo(0, 0)?Carib
@Carib Either way is fine.window just refers to the root and document.documentElement just refers to the <html> 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.Mulligatawny
this does not work in 2022 JulyTaber
@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.Mulligatawny
for a smooth scroll document.querySelector('body').scrollIntoView({behavior: 'smooth'});Microminiaturization
Why react router doesn't handle it?Spaghetti
U
8

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

Updraft answered 14/10, 2022 at 19:51 Comment(1)
I used this solution and it works like a charm! NOTE: I had to import the <ScrollToTop/> 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.Feuchtwanger
A
5

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
Aromatize answered 22/3, 2023 at 6:21 Comment(0)
B
4

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). It requires using a data router, such as one created by calling createBrowserRouter (which is recommended for all React Router web projects).

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/>
    </>;
}
Barnes answered 17/3, 2023 at 18:41 Comment(0)
T
2

This 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.

Twosided answered 16/11, 2022 at 21:26 Comment(0)
Z
1

The solution is to render the component <ScrollRestoration /> inside your "root" component, see the next code:

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

function RootRouteComponent() {
  return (
    <div>
      {/* ... */}
      <ScrollRestoration />
    </div>
  );
}

You can see more details: here

Zionism answered 6/2, 2024 at 23:8 Comment(0)
D
1

In react-router-dom v6 you can do it this way

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

    useEffect(() => {
        window.scrollTo(0, 0);
    }, [pathname]); // Only re-run the effect if the pathname changes

    return null;
    }

    export default function ScrollToTop() {
    return (
        <BrowserRouter>
        <ScrollToTop />
        <Routes onUpdate={() => window.scrollTo(0, 0)}>
            <Route path="*" element={<Pagenotfound />} />
             // ...your other routes will go here
        </Routes>
        </BrowserRouter>
    );
    }
Dorking answered 13/5, 2024 at 10:4 Comment(0)
C
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.

Critta answered 2/12, 2021 at 3:6 Comment(0)
D
0

i don`t know how but in my case ScrollRestoration works only in Layout, not inside App etc...

import { Outlet, ScrollRestoration } from "react-router-dom";

import { Header } from "../Header/Header.jsx";
import { Footer } from "../Footer/Footer.jsx";

export const Layout = () => {
  return (
    <>
      <Header />
      <main>
        <Outlet />
      </main>
      <Footer />
      <ScrollRestoration />
    </>
  );
}; ``` 
i use "react-router-dom": "^6.21.2"
Daedalus answered 17/2, 2024 at 18:37 Comment(0)
P
0

i solved with custom hook

// useScrollTop.jsx
import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export default function UseScrollTop() {
  const location = useLocation();

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

  return null;
}

in App.jsx or Layout.jsx

import UseScrollTop from '@/hooks/useScrollTop';

const Layout = () => {
    UseScrollTop()
    return <>
        .....
    </>
}

export default Layout
Proffitt answered 15/3, 2024 at 13:15 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.