DEV Community

Daniel Gruner
Daniel Gruner

Posted on

Scroll to anchor element with React Router V6 (hash links, anchor links)

Hi! With inspiration of Nick Coughlin, I developed a pretty simple solution for using hash links or anchor links with React Router V6. As Nick proposes, it's without any further dependency like the popular react-router-hash-links.

Target

Goal is that your app scrolls to a certain element with a certain ID, when a so called hash link or anchor link is used.

Example:

...
// react-router Link component
<Link to="/mypage#myheadline">Press Link to get to headline</Link>
...
Enter fullscreen mode Exit fullscreen mode

By clicking this link, the user expects to be navigated to "/mypage" and scrolled to the element with ID "myheadline"

...
// headline with ID "myheadline"
<h2 id="myheadline">My Headline</h2>
...
Enter fullscreen mode Exit fullscreen mode

ScrollToAnchor.tsx or jsx

Put this component anywhere within your router component. It simply listens for location changes and determines if there is a hash (or element ID) that your app should scroll to.

import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';

function ScrollToAnchor() {
  const location = useLocation();
  const lastHash = useRef('');

  // listen to location change using useEffect with location as dependency
  // https://jasonwatmore.com/react-router-v6-listen-to-location-route-change-without-history-listen
  useEffect(() => {
    if (location.hash) {
      lastHash.current = location.hash.slice(1); // safe hash for further use after navigation
    }

    if (lastHash.current && document.getElementById(lastHash.current)) {
      setTimeout(() => {
        document
          .getElementById(lastHash.current)
          ?.scrollIntoView({ behavior: 'smooth', block: 'start' });
        lastHash.current = '';
      }, 100);
    }
  }, [location]);

  return null;
}

export default ScrollToAnchor;
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
ncoughlin profile image
Nick Coughlin

This component has been converted to an NPM package with the following improvements:

  • no longer requires react-router as a dependency
  • typescript compatible

You can view the github repo here:
github > ncoughlin/scroll-to-hash-element

and the NPM repo here:
npm > scroll-to-hash-element

Collapse
 
davidd344 profile image
David Freitas • Edited

if you want scroll for after navbar, it's work for me:


import { useEffect, useRef } from 'react'
import { useLocation } from 'react-router-dom'

function ScrollToAnchor (): null {
  const location = useLocation()
  const lastHash = useRef('')
  const navbarHeight = 110
  console.log(location, lastHash)
  useEffect(() => {
    if (location.hash.length > 0) {
      lastHash.current = location.hash.slice(1)
    }

    if ((lastHash.current.length > 0) && (document.getElementById(lastHash.current) != null)) {
      setTimeout(() => {
        //  s
        const element = document
          .getElementById(lastHash.current)
        // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
        if (element) {
          const elementPosition = element.getBoundingClientRect().top + window.scrollY
          window.scrollTo({
            top: elementPosition - navbarHeight,
            behavior: 'smooth'
          })
        }
        // s
      }, 100)
    }
  }, [location])

  return null
}

export default ScrollToAnchor
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ktechgau profile image
Karla

Hello and thank you for this! Pardon my ignorance I am new to React JS, I have created a component, imported the function to the page I want to use it, but still not working, what am I missing please?

Collapse
 
fadelyang profile image
Fadela Numah Kadenz

It's simple and works, Thankyou