I've been trying to implement the "scrollToTop" feature/component in my React project but to no avail.
A. I don't know where to put the ScrollToTop
component suggested to be used in the docs (https://v5.reactrouter.com/web/guides/scroll-restoration) in the router provider
import "./App.css";
import {
Route,
RouterProvider,
createBrowserRouter,
createRoutesFromElements,
} from "react-router-dom";
import Layout from "./components/Layout";
import Home from "./pages/Home";
import Day1 from "./pages/Day1";
import Day2 from "./pages/Day2";
import Day3 from "./pages/Day3";
import DayC from "./pages/DayC";
export default function App() {
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<Layout />}>
{/* If I put it somewhere here it returns an error stating that all components children of routes must be <Route> or <Fragment> */}
<Route index element={<Home />} />
<Route path="day1" element={<Day1 />} />
<Route path="day2" element={<Day2 />} />
<Route path="day3" element={<Day3 />} />
<Route path="dayC" element={<DayC />} />
</Route>
)
);
return (
<>
<RouterProvider router={router} />
</>
);
}
B. By itself, rendering the <ScrollToTop>
component returns the error "useLocation can only be used in the context of a Route component"
I don't know how to use it correctly. My desired effect is just for the page to scroll upwards on navigation since the content on the landing page is pretty lengthy.
Any React component rendering or using react-router-dom
components or hooks necessarily needs to be rendered/called within the sub-ReactTree and context provided by a router. In this case it is the RouterProvider
component.
You can't call/use ScrollToTop
directly in createBrowserRouter
or createRoutesFromElements
as these functions expect a RouteObject[]
or JSX of Route
s respectively.
Either add the logic to the root Layout
route component, or create another layout route component specifically for this purpose.
Examples:
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;
}
import { Outlet } from 'react-router-dom';
import { ScrollToTop } from '../components';
const Layout = () => {
...
return (
...
<ScrollToTop />
...
<Outlet />
...
);
};
or as a hook
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function useScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
}
import { Outlet } from 'react-router-dom';
import { useScrollToTop } from '../hooks';
const Layout = () => {
...
useScrollToTop();
...
return (
...
<Outlet />
...
);
};
As a layout route
import { useEffect } from "react";
import { Outlet, useLocation } from "react-router-dom";
export default function ScrollToTopLayout() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return <Outlet />;
}
const router = createBrowserRouter(
createRoutesFromElements(
<Route element={<ScrollToTopLayout />}>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="day1" element={<Day1 />} />
<Route path="day2" element={<Day2 />} />
<Route path="day3" element={<Day3 />} />
<Route path="dayC" element={<DayC />} />
</Route>
</Route>
)
);
export default function App() {
return <RouterProvider router={router} />;
}
There also already exists a RRDv6 ScrollRestoration
component that should scroll back to the top of the screen upon new location keys (i.e. a new entry in the history stack), and restores the scroll position upon navigating back to a previous page. It only works in Data routers, e.g. the createBrowserRouter
you are using, and only needs to be rendered once in the root of the app, e.g. in Layout
.
import { Outlet, ScrollRestoration } from 'react-router-dom';
const Layout = () => {
...
return (
...
<ScrollRestoration />
...
<Outlet />
...
);
};
Review the ScrollRestoration
docs for further customizations.