skip to Main Content

I’m using React-Router with a custom ScrollToTop component to reset the scroll position to the top of the page on navigation. However, I have a specific use case where I need the scroll position to stay the same when clicking on category item.

ScrollToTop:

export const ScrollToTop = () => {
  const { pathname } = useLocation();

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

  return null;
};

Router:

<BrowserRouter>
  <ScrollToTop />
  <Routes>
    <Route path="/" element={<Layout />}>
      <Route path="/" element={<HomePage />}>
        <Route path=":category" element={<ExclusiveOffers />} />
      </Route>
      <Route path=":category/all" element={<AllProductsPage />} />
      <Route path=":category/:name" element={<ProductPage />} />
      <Route path="/account" element={<AccountPage />} />
      <Route path="/checkout" element={<CheckoutPage />} />
    </Route>
    <Route path="/auth" element={<AuthPage />} />
    <Route path="/login" element={<LoginPage />} />
    <Route path="/register" element={<RegisterPage />} />
  </Routes>
</BrowserRouter>

Category item:

<Box
  component={Link}
  to={title === "Top Categories"
    ? `/${item.query}`
    : `/${item.query}/all`
  }

2

Answers


  1. React Router works on maintaining the same scroll position if we navigate from one route to another. To prevent those we use custom scroll like you did and use overall on routes in app.

    export const ScrollToTop = () => {
      const { pathname } = useLocation();
    
      useEffect(() => window.scrollTo(0, 0), [pathname]);
    
      return null;
    };
    

    For your use case like category item to bypass the custom scroll you can do the following modifications.

    1) App.jsx

    const App=()=>{
    const excludedPaths= ['/path-you-dont-want-custom-scrolltop']
    return (
    <BrowserRouter>
        <ScrollToTop excludedPaths={excludedPaths}/>
        <Routes>
          <Route path="/" element={<Layout />}>
            <Route path="/" element={<HomePage />}>
              <Route path=":category" element={<ExclusiveOffers />} />
            </Route>
            <Route path=":category/all" element={<AllProductsPage />} />
            <Route path=":category/:name" element={<ProductPage />} />
            <Route path="/account" element={<AccountPage />} />
            <Route path="/checkout" element={<CheckoutPage />} />
          </Route>
          <Route path="/auth" element={<AuthPage />} />
          <Route path="/login" element={<LoginPage />} />
          <Route path="/register" element={<RegisterPage />} />
        </Routes>
      </BrowserRouter>
    )
    }
    
    

    2) ScrollToTop.jsx

    Replace the below with your scroll to top

    import { useEffect } from "react";
    import { useLocation } from "react-router-dom";
    import PropTypes from 'prop-types';
    
    export const ScrollToTop = ({excludedPaths}) => {
        const { pathname } = useLocation();
        
      useEffect(() => {
        if (!excludedPaths?.includes(pathname)) {
          window.scrollTo(0, 0);
        }
      }, [pathname, excludedPaths]);
    
      return null;
    };
    
    ScrollToTop.propTypes = {
      excludedPaths: PropTypes.arrayOf(PropTypes.string)
    };
    
    

    It will exclude the path which you don’t want to apply scroll top to. Let me know if this fix the problem.

    Login or Signup to reply.
  2. You can accomplish skipping resetting the scroll-to-top a few different ways:

    1. Convert ScrollToTop to a layout route to wrap only the routes you want to scroll-to-top:

      import { Outlet } from 'react-router-dom';
      
      export const ScrollToTopLayout = () => {
        const { pathname } = useLocation();
      
        useEffect(() => window.scrollTo(0, 0), [pathname]);
      
        return <Outlet />;
      };
      

      Example Usage:

      <BrowserRouter>
        <Routes>
          <Route element={<Layout />}>
            <Route element={<ScrollToTopLayout />}>
              <Route index element={<HomePage />} />
              <Route path=":category/:name" element={<ProductPage />} />
              <Route path="account" element={<AccountPage />} />
              <Route path="checkout" element={<CheckoutPage />} />
            </Route>
      
            <Route path=":category" element={<ExclusiveOffers />} />
            <Route path=":category/all" element={<AllProductsPage />} />
          </Route>
      
          <Route element={<ScrollToTopLayout />}>
            <Route path="auth" element={<AuthPage />} />
            <Route path="login" element={<LoginPage />} />
            <Route path="register" element={<RegisterPage />} />
          </Route>
        </Routes>
      </BrowserRouter>
      
    2. Update ScrollToTop to consume explicit route paths to skip via props:

      import { useLocation, matchPath } from 'react-router-dom';
      
      export const ScrollToTop = ({ excludePaths = []}) => {
        const { pathname } = useLocation();
      
        useEffect(() => {
          const isExcluded = excludePaths.some(
            pattern => matchPath(pattern, pathname)
          );
      
          if (!isExcluded) {
            window.scrollTo(0, 0);
          }
        }, [excludePaths, pathname]);
      
        return null;
      };
      

      Example Usage:

      <BrowserRouter>
        <ScrollToTop excludePaths={[":category/all", ":category"]} />
      </BrowserRouter>
      

      Note however that ":category" would match other non-"category" route paths, like "/account" and "/login", so may not be ideal for all scenarios.

    3. Pass some link state to skip scrolling to top upon navigating:

      export const ScrollToTop = () => {
        const { pathname, state } = useLocation();
      
        useEffect(() => {
          if (!state?.preventScrollToTop) {
            window.scrollTo(0, 0);
          }
        }, [pathname, state]);
      
        return null;
      };
      

      Example Usage:

      <Box
        component={Link}
        to={title === "Top Categories"
          ? `/${item.query}`
          : `/${item.query}/all`
        }
        state={{ preventScrollToTop: true }}
      >
        ....
      </Box>
      

    You can use, mix, and apply any of the above implementations to cover more scenarios.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search