BlogReactJS

ReactJS header that disappears on scroll down and appears on scroll up

Written by Codemzy on February 23rd, 2022

Want the convenience of easy access to a sticker header, without it getting in your way all of the time? Here's how you can hide your sticky header when scrolling down, and get it back as soon as you scroll up!

Sticky headers are pretty great for long pages since you don’t have to scroll all the way back to the top to access the menu. It always stays on the screen. But this can also be a bit obstructive, especially if the header is big.

One way to get the benefits of a sticky header, and overcome the drawbacks, is to have the header disappear as you scroll down, but appear again when you start to scroll back up.

I'd seen this on a few websites and always thought it looks super cool. Until now, I hadn't tried to implement it myself. Because I thought it would be fairly complex to do. But it's actually not that hard!

Here’s how it looks:

See the Pen Sticky Disappearing Header ReactJS by Codemzy (@codemzy) on CodePen.

You can see the header disappear as you scroll down, letting you focus on the content of the page. But, when you want to see the header again, you only have to scroll up a little to get it back.

I use ReactJS as the JavaScript framework and built a custom scroll hook. You could achieve the same thing using vanilla JavaScript too - it uses event listeners to check scroll direction (more on that when we dive into the code).

And I use TailwindCSS to make the page look pretty. Check it out if you’ve not already. But I’ll also include the vanilla CSS to get you started if you don’t use TailwindCSS.

Header Component

Let’s start by creating a sticky header. You know, the ones you commonly see on websites where the navigation menu stays at the top of the page when you scroll down.

I know, I know, we don’t want it to stay sticky when we scroll down. But we do want it sticky when we scroll up. So sticky is where we start!

In TailwindCSS, you can add the sticky top-0 class to your header element, and it will be sticky at the top of the page! Or you can add it with CSS.

.header {
  position: sticky;
  top: 0px;
}

Make sure your sticky header isn't nested in any other div or is at least in a div that is the full height of the body if you want to stay sticky as you scroll the entire page.

One of the things that will make the disappearing aspect of the header much easier, later on, is having a fixed height*. In the example below, h-24 specifies the height.

// header component
function Header() {
  return (
    <div className="sticky top-0 h-24 bg-blue-200">
      <div className="p-5 font-bold">Disappearing Header</div>
    </div>
  );
};

Or if you are writing your CSS, you can add a height.

.header {
  position: sticky;
  top: 0px;
  height: 6rem;
}

*You could get around this by getting the height using JavaScript, but it will add extra complexity to the disappearing part!

If your header needs different sizes for different screens, e.g. deeper on a mobile, you can set a different height for smaller screens. This still works well. You just need to know what height your header is at each screen size.

Ok, now you have a sticky header. But it's always visible. It doesn't disappear. Let's get to the magic.

Disappear On Scroll

We want the header to disappear as the user scrolls down the screen, and reappear when they scroll up. Without having to go back to the top as you would with a traditional static header.

So we will need to know:

  • When a scroll is happening
  • What direction

Luckily, I already wrote a post for a ReactJS hook that listens to scroll direction. So we can borrow that hook!

function useScrollDirection() {
  const [scrollDirection, setScrollDirection] = useState(null);

  useEffect(() => {
    let lastScrollY = window.pageYOffset;

    const updateScrollDirection = () => {
      const scrollY = window.pageYOffset;
      const direction = scrollY > lastScrollY ? "down" : "up";
      if (direction !== scrollDirection && (scrollY - lastScrollY > 10 || scrollY - lastScrollY < -10)) {
        setScrollDirection(direction);
      }
      lastScrollY = scrollY > 0 ? scrollY : 0;
    };
    window.addEventListener("scroll", updateScrollDirection); // add event listener
    return () => {
      window.removeEventListener("scroll", updateScrollDirection); // clean up
    }
  }, [scrollDirection]);

  return scrollDirection;
};

You can learn more about the scroll direction hook if you want to know more about how it works.

Now we can wire up that hook to our header component, and add a CSS class that hides the header when the scroll direction is "down". You can do this by changing the position from top-0 to -top-* where * is whatever height your header is.

// header component
function Header() {
  const scrollDirection = useScrollDirection();
  
  return (
    <div className={`sticky ${ scrollDirection === "down" ? "-top-24" : "top-0"} h-24 bg-blue-200`}>
      <div className="p-5 font-bold">Disappearing Header</div>
    </div>
  );
};

This also works if your header is a different height on different screens. For example, if your header is bigger on mobile h-28 md:h-24 you can use ${ scrollDirection === "down" ? "-top-28 md:-top-24" : "top-0"}.

It's pretty jumpy though, so let's add a transition transition-all duration-500 to make it smoother.

// header component
function Header() {
  const scrollDirection = useScrollDirection();
  
  return (
    <div className={`sticky ${ scrollDirection === "down" ? "-top-24" : "top-0"} h-24 bg-blue-200 transition-all duration-500`}>
      <div className="p-5 font-bold">Disappearing Header</div>
    </div>
  );
};

Without TailwindCSS

If you're not using TailwindCSS, your component will look more like this:

// header component
function Header() {
  const scrollDirection = useScrollDirection();
  
  return (
    <div className={`header ${ scrollDirection === "down" ? "hide" : "show"}`}>
      <div>Disappearing Header</div>
    </div>
  );
};

And your CSS like this:

.header {
  position: sticky;
  top: 0px;
  background-color: #BFDBFE;
  height: 6rem;
  transition-property: all; 
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 500ms;
}

.header.down {
  top: -6rem;
}

And that's it! A sticky header that disappears when you scroll down and re-appears when you scroll up!