How to navigate to another page with a smooth scroll on a specific id with react router and react scroll

Viewed 8770

i am trying to make navBar that navigate to another page and scroll down to some id on this page

this is the navBar component for my protofolio whih have 3 links the Projects should go to Home and then scroll down to projects id in the Project section

import React, { Component } from "react";
import logo from "../../images/logo.png";
import { Link } from "react-router-dom";
import Scroll from "react-scroll";
import "./NavBar.css";
const ScrollLink = Scroll.Link;

class NavBar extends Component {
  render() {
    return (
      <div className="NavBar">
        <Link to="/" className="logo-container">
          <img className="logo" src={logo} alt="Mahboub logo"></img>
        </Link>
        <div className="Nav-links">

          <Link to="/">Home</Link>
          <ScrollLink
            className="navy"
            smooth={true}
            duration={500}
            to="projects"
          >
            projects
          </ScrollLink>

          <Link to="/">Contacts</Link>
        </div>
      </div>
    );
  }
}
export default NavBar;

here is the prjects component for project section in home page

function Projects() {
  return (
    <div className="Home-Projects">
      <Element id="projects">
        <h1>Projects</h1>
      </Element>
      <div className="Projects-container">
        <ProjectElement />
      </div>
    </div>
  );
}
5 Answers

If "duration" parameter isn't of great importance for you and you have no problems with Hooks, I'd like to suggest solution as follows.

  • You get rid of Scroll component and use plain Link from RR in your NavBar like so

    <Link to="/#projects">projects</Link>
    
  • Since RR v5.1 Hooks was introduced. There is useLocation hook among them. Insert that hook at the very beginning of your Projects component like so:

    function Projects() {
    const location = useLocation()
    ...
    

When you hit Link from p.1, you get new location object of the form


    location = {
        ...
        pathname:"/",
        ...
        hash: "#projects"
    }

  • Call useEffect hook after useLocation like so:

    useEffect(()=> {
            if (location.hash) {
                let elem = document.getElementById(location.hash.slice(1))
                if (elem) {
                    elem.scrollIntoView({behavior: "smooth"})
                }
            } else {
            window.scrollTo({top:0,left:0, behavior: "smooth"})
            }
    }, [location,])

And thats it. As you can see, if there is no hash in your url, your page would scroll to the top. Use [locaton,] as useEffect dependency prevents form scrolling when your page component rerenders not because of location changes.

I just add this in the index.css

html {
  scroll-behavior: smooth;
}

and this

 <ScrollLink
            className="navy"
            smooth={true}
            duration={500}
            to="projects"
          >
            projects
 </ScrollLink>

became

 <a href="/#projects">Projects</a>

this was so fast and simple and worked prefectly

For next.js

//https://yourdomain.com/page?scroll=scroll_here <---

import { useEffect } from 'react';
import { Flex } from '@chakra-ui/react';
import { useRouter } from 'next/router';
export default function DecisionAreaFlex(props) {
  const router = useRouter();
  useEffect(() => {
    console.log('router query', router.query.scroll);
    if (router.query.scroll) {
      let elem = document.getElementById(router.query.scroll);
      if (elem) {
        elem.scrollIntoView({ behavior: 'smooth' });
      }
    } else {
      window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
    }
  }, [router]);

  return (
    <Flex
      alignSelf="center"
      justifyContent="center"
      alignItems="center"
      mt="15px"
      id={'scroll_here'} // <--- 
    >
      {props.children}
    </Flex>
  );
}

I've created a query param called anchor, and with that I can control the scroll based on the useEffect.

The link: /about?anchor=capabilities

The code on the page which should have the smooth scroll:

const router = useRouter();

useEffect(()=> {
  if (router?.query?.anchor) {
    let elem = document.getElementById(router?.query?.anchor)
    if (elem) {
      elem.scrollIntoView({behavior: "smooth"});
    }
  }
}, [router?.query?.anchor])

here is the simplest solution for scrolling to an id from another page. use this code on the page where you have sections with any xyz id. now on the other pages use simple NavLink from react-router-dom having "to" attribute like this to="/#xy"

 let location = useLocation()
   useEffect(()=> {
    if (location.hash) {
      let elem = document.getElementById(location.hash.slice(1))
      if(elem) {
        elem.scrollIntoView({behavior: "smooth"})
               }          
        } else {
  window.scrollTo({top:0,left:0, behavior: "smooth"})
                }
    }, [location,])
Related