DEV Community

Derick Ify Iloabachie
Derick Ify Iloabachie

Posted on • Updated on

Smart Navbar With React And Sass

Introduction

This tutorial assumes you have some knowledge on React and Sass (scss).

Sticky or fixed navigation bars is a popular design choice since it gives users an easy-to-reach access to navigate a web application or website.
However, it takes up space especially on smaller screens and might cover content in a way that's not appealing.

Possible Solution :- Smart Navbar

A smart navbar stays visible when a user is at the top of the page. The navbar is also visible when the user makes a scroll upwards from wherever they are on the page and it is hidden when the user makes a scroll downwards of the page.

Let's look at the code for the .jsx

function Navbar(){

  return (
    <nav className='nav'>
      <div className='mainDiv'>
        <span>
          Logo
        </span>

        <ul>
          <li>Link 1</li>
          <li>Link 2</li>
          <li>Link 3</li>
        </ul>
      </div>
    </nav>
}
Enter fullscreen mode Exit fullscreen mode

And the code for the .css

.nav {
  position: fixed;
  width: 100%;
  z-index: 999;
  top: 0;
  right: 0;
  left: 0;
  padding: 0.3rem 0;
  transition: all 200ms ease-in-out;

  .mainDiv {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
}
Enter fullscreen mode Exit fullscreen mode

We can define some state variables to keep track of the nav and modify it accordingly

const [atTop, setAtTop] = useState(true);
const [scrollDirection, setScrollDirection] = useState('');
Enter fullscreen mode Exit fullscreen mode

We need to detect when a user is scrolling the page and the direction of their scroll. A user is scrolling down if the value of their last scroll position is less than the value of their current scroll position.

useEffect(() => {
    const threshold = 0;
    let lastScrollY = window.pageYOffset;
    let ticking = false;

    const updateScrollDirection = () => {
      const scrollY = window.pageYOffset;

      if (Math.abs(scrollY - lastScrollY) < threshold) {
        ticking = false;
        return;
      }
      setScrollDirection(scrollY > lastScrollY ? "down" : "up");
      lastScrollY = scrollY > 0 ? scrollY : 0;
      ticking = false;
    };

    const onScroll = () => {
      if (!ticking) {
        window.requestAnimationFrame(updateScrollDirection);
        ticking = true;
      }
    };

    window.addEventListener("scroll", onScroll);

    return () => window.removeEventListener("scroll", onScroll);
  }, [scrollDirection]);
Enter fullscreen mode Exit fullscreen mode

We can use this logic to detect when the page is scrolling down and toggle our styling

// navbar.jsx

<nav className={'nav ${scrollDir === 'down' ? 'isScrollingDown' : '' }'}>
...
</nav>
Enter fullscreen mode Exit fullscreen mode

If the user is scrolling down we add the .isScrollingDown class and if the user is scrolling up, we simply remove the class

// style.scss

.isScrollingDown {
  transform: translateY(-100%);
}
Enter fullscreen mode Exit fullscreen mode

Performance

When working with scroll events, it is important to pay great attention to performance as we might call a function too many times which can have adverse effects.
In the above useEffect we only update the state scrollDirection when the user scrolls in the opposite direction.

We can also check for when the user is at the top of the page and utilize a throttle function to update styles or perform other logic.

Briefly, a throttle function can be defined as a higher order function that acts as a timer for the function passed into it.
Let's see an example of a throttle function

// initialize a throttleWait variable for performance
  let throttleWait;

  const throttle = (callback, time) => {
    // if the variable is true, don't run the function
    if (throttleWait) return;

    // set the wait variable to true to pause the function
    throttleWait = true;

    // use setTimeout to run the function within the specified time
    setTimeout(() => {
      callback();

      // set throttleWait to false once the timer is up to restart the throttle function
      throttleWait = false;
    }, time);
  };
Enter fullscreen mode Exit fullscreen mode

We can make use of the throttle function in a useEffect to perform logic as we wish.

const navbarControl = () => {
   if (window.scrollY < 50) {
     setAtTop(true);
   } else {
     setAtTop(false);
   }
};

useEffect(() => {
    window.addEventListener("scroll", () => throttle(navbarControl, 500));

    return () => {
      window.removeEventListener("scroll", throttle);
    };
  }, []);
Enter fullscreen mode Exit fullscreen mode

With this implementation, the navbarControl function is called only once every 500 milliseconds

Conclusion

So, there we have it: a smart navigation implementation with React and Sass for styling. Now users have easy access to navigate the web app/website without losing real estate in a way that blocks content on the page.

Top comments (0)