How to create a grow-shrink header in React

How to create a grow-shrink header in React

·

3 min read

Hello World!

Let me show you how we can create a header, that shrinks when we scroll down and expands when we scroll back to the top.

Steps to achieve the goal

  1. Listen to the page scroll.

  2. When scrolled beyond a point, shrink the header.

  3. When scrolling back to the top, reset the header to normal.

  4. Add some transition to make the grow/shrink smooth

Implementation

Listening to page scroll

We can listen to the scroll using the event listeners. We will have to start listening to the scroll when the component mounts and will have to stop listening when the component unmounts. For this, we can make use of the useEffect hooks.

useEffect(() => {
    // Adding the scroll listener
    window.addEventListener('scroll', handleScroll, { passive: true });

    return () => {
        // Removing listener
        window.removeEventListener('scroll', handleScroll);
    };
}, []);

passive is used to denote that an event is a passive event and the handler won't call the preventDefault to disable scrolling.

Handler implementation

To know whether the page is scrolled or not, we can use a flag called isScrolled . The isScrolled is to be set to true, when the screen is scrolled beyond a point (250 for example) and set to false when scrolled back to the top (when the scroll position is less than 250). We can track the scroll position with window.pageYOffset .

So the handler function would be:

// Flag, which stores whether the screen is scrolled
const [isScrolled, setScrolled] = useState(false);

// Handler when page is scrolled
const handleScroll = () => {
  if(window.pageYOffset > 250) {
    setScrolled(true)
  } else {
    setScrolled(false)
  }
}

Component Implementation

Now we can sense whether the page is scrolled or not using the isScrolled flag. Now we can implement the header component markup like:

return (
    <div className={`header-container ${isScrolled && 'header-scrolled'}`}>
      <h1>SR</h1>
      <h2>Sriram</h2>
    </div>
  );

Here we are injecting the header-scrolled class when the screen is scrolled. That is when the isScrolled is true.

Let us see the styling of the component:

.header-container {
  display: flex;
  align-items: center;
  background: $header-color;
  box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset;
  height: 7rem;
}
.header-scrolled {
  height: 3rem;
}

Here in the header-container class, the height is set to 7rem . When the page is scrolled and header-scrolled class is injected, the height would be replaced with 3rem , which would shrink the header vertically.

But the grow/shrink transition would be sharp now. We can add some transition in the CSS to make the grow/shrink smooth.

.header-container {
  display: flex;
  align-items: center;
  background: $header-color;
  box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset;
  height: 7rem;
  // To make the grow/shrink smooth.
  transition: height 2s;
}

That's it, we are done! Now when we scroll the screen, the header will grow and shrink smoothly.

The final code would be:

import React, { useEffect, useState } from "react";
import "./header.scss";

export default function Header() {
  const [isScrolled, setScrolled] = useState(false);
  useEffect(() => {
    window.addEventListener('scroll', handleScroll, { passive: true });

    return () => {
        window.removeEventListener('scroll', handleScroll);
    };
}, []);
const handleScroll = () => {
  if(window.pageYOffset > 250) {
    setScrolled(true)
  } else {
    setScrolled(false)
  }
}
  return (
    <div className={`header-container ${isScrolled && 'header-scrolled'}`}>
      <h1>SR</h1>
      <h2>Sriram</h2>
    </div>
  );
}
.header-container {
  display: flex;
  align-items: center;
  background: $header-color;
  box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset;
  height: 7rem;
  transition: height 2s;
}
.header-scrolled {
  height: 3rem;
}

Hope this is helpful to you. Looking forward to your feedback, Thank you!