How to scroll to a section in new page using react

May 18, 2023

Set Up

We will need to setup React and this will be done using vite ( a build tool for modern web projects). Go to your CLI and install vite. Note, you'll need need nodejs install for the npm command line tools

With NPM 7+:

npm create vite@latest my-react-app -- --template react

With Yarn:

yarn create vite@latest my-react-app -- --template react

Then run the following prompts

cd my-react-app

npm install
npm run dev

We can see the response we have below in our CLI, this shows that dev server is ready.

VITE v4.3.8  ready in 1025 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help

One more package we need to install is react-router-dom which allows us to define route & navigate to different pages in your application"

With NPM:

npm install react-router-dom

With Yarn:

yarn add react-router-dom

Our setup is pretty much done, now let's move forward

Our Folder Structure

This is the folder structure found in src/

src
|⁠—⁠—⁠— assets
     |⁠—⁠—⁠— react.svg
|⁠—⁠—⁠— App.css
|⁠—⁠—⁠— App.jsx
|⁠—⁠—⁠— index.css
|⁠—⁠—⁠— main.jsx

We need to delete unsed file like index.css and we end up having something like this

src
|⁠—⁠—⁠— assets
     |⁠—⁠—⁠— react.svg
|⁠—⁠—⁠— App.css
|⁠—⁠—⁠— App.jsx
|⁠—⁠—⁠— main.jsx

Defining our routes

In the main.js file, we import BrowserRouter from react-router-dom and wrap our whole app with it.

//main.jsx

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

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

We need to create two new files (Home.jsx and About.jsx) in our src folder. When that's done, let's go back to our App.js where we will import the <Routes> and <Route>components from react-router-dom.

//App.jsx

import { Routes, Route } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import "./App.css";

const App = () => {
  return (
    <Routes>
      <Route path="/" index element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
  );
};

export default App;

What we've done here is when the location changes we use <Routes> to looks through all its child routes to find the best match and renders that branch of the UI.

How It Works

In our Home.jsx file, import Link component from react-router-dom and use it to define the link to the about page.

We use the # symbol followed by the id of the section we want to scroll to (section2 of the about page in this case).

//Home.jsx

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

const Home = () => {
  return (
    <div>
      <Link to={"/about#section2"}>About Page</Link>
    </div>
  );
};

export default Home;

Scrolling to section

In the component that contains the section we want to scroll to (About.jsx in this example), We check if id is present in the URL. We can do this inside the useEffect Hook

//About.jsx

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

const About = () => {
  const location = useLocation();
  const { hash } = location;

  useEffect(() => {
    if (hash) {
      const section = document.querySelector(hash);
      if (section) {
        section.scrollIntoView({ behavior: "smooth" });
      }
    } else {
      window.scroll(0, 0);
    }
  }, [hash]);

  return (
    <div>
      <div
        style={{
          width: "100vw",
          height: "100vh",
          display: "grid",
          placeContent: "center",
          background: "gray",
        }}
      >
        <div style={{ textAlign: "center" }}>
          <h1>Section 1</h1>
          <p>
            Lorem ipsum, dolor sit amet consectetur adipisicing elit. Rerum
            odio, facilis iste quaerat quisquam ex mollitia impedit dolore.
            Deleniti, optio!
          </p>
        </div>
      </div>
      <div
        id="section2"
        style={{
          width: "100vw",
          height: "100vh",
          display: "grid",
          placeContent: "center",
          background: "#fcfcfc",
        }}
      >
        <div style={{ textAlign: "center" }}>
          <h1>Section 2</h1>
          <p>
            Lorem ipsum, dolor sit amet consectetur adipisicing elit. Rerum
            odio, facilis iste quaerat quisquam ex mollitia impedit dolore.
            Deleniti, optio!
          </p>
        </div>
      </div>
    </div>
  );
};

export default About;

We use the useLocation hook from react-router-dom to get access to the current location object, which includes the hash value of the URL (if any). Then we check if the hash value is present, scroll to section2 otherwise scroll to top