Back to overview

Highlighting navigation items on scroll in React with IntersectionObserver

Compass lying on a physical map
| 2 min read
View count:

This week I was working on my personal website (, 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 (

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);

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(
        if (entry.isIntersecting) {
          if (!navElement.classList.contains("active")) {
        } else if (navElement.classList.contains("active")) {
    }, options);
  return () => observer.disconnect();
}, [personalRef, portfolioRef, contactRef]);

And that’s it! The active class will be added to the navigation element which points to the <>. 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

Did you like this post? Check out my latest blog posts:

Screenshot of an analytics dashboard
16 Jan 2024

Basic analytics with Vercel Postgres - Drizzle - Astro

10 min reading time

  • vercel
  • databases
  • typescript
  • astro
Page of a dictionary
15 Nov 2023

A minimal dependency-free translation system for Next.js

6 min reading time

  • nextjs
  • react
  • javascript
Chrome logo repeated in a grid
4 Sept 2023

Developing a Chrome extension to convert the current url to localhost in 1 click

4 min reading time

  • javascript
  • chrome extension
  • html