DEV Community

Thomas Ledoux
Thomas Ledoux

Posted on • Edited on • Originally published at thomasledoux.be

5 3

Highlighting navigation items on scroll in React with IntersectionObserver

This week I was working on my personal website (https://www.thomasledoux.be), and I needed my navigation items to be highlighted when scrolling to the linked section.
I found some solutions with a scroll listener, but none using the widely supported Intersection Observer (https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).

So I decided to write the logic myself.
What has to be done first, is referencing the <section>'s using React.useRef.

import * as React from 'react'
const personalRef = React.useRef(null)
const portfolioRef = React.useRef(null)
const contactRef = React.useRef(null)
Enter fullscreen mode Exit fullscreen mode

Now we have the reference to the <section>'s we can fire up the IntersectionObserver. I prefer to do this using the React.useEffect hook. The ref objects are added as dependencies, so we can reference these when they are ready. I use the 0.5 threshold, this will cause the observer to be triggered when the <section> is >50% visible. The navElement gets the navigation element which has a href pointing to the <section>'s id.

React.useEffect(() => {
    let observer
    if (personalRef.current && portfolioRef.current && contactRef.current) {
      const options = {
        threshold: 0.5,
      }
      observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
          const navElement = document.querySelector(
            `a[href="/#${entry.target.id}"]`,
          )
          if (entry.isIntersecting) {
            if (!navElement.classList.contains('active')) {
              navElement.classList.add('active')
            }
          } else if (navElement.classList.contains('active')) {
            navElement.classList.remove('active')
          }
        })
      }, options)
      observer.observe(personalRef.current)
      observer.observe(portfolioRef.current)
      observer.observe(contactRef.current)
    }
    return () => observer.disconnect()
  }, [personalRef, portfolioRef, contactRef])
Enter fullscreen mode Exit fullscreen mode

And that's it! The active class will be added to the navigation element which points to the <section>.
By adding the return function at the end of the useEffect hook, we make sure the IntersectionObserver is cleaned up correctly.

Full code can be found on https://github.com/thomasledoux1/website-thomas

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay