paint-brush
Creating a Dynamic Header in React: Shrink on Scroll, Expand on Topby@sriram
501 reads
501 reads

Creating a Dynamic Header in React: Shrink on Scroll, Expand on Top

by SriramNovember 3rd, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

How to create a grow-shrink header in React. Listen to the page scroll. When scrolled beyond a point, shrink the header. When scrolling back to the top, reset the header to normal. Add some transition to make the grow/shrink smooth.

People Mentioned

Mention Thumbnail
featured image - Creating a Dynamic Header in React: Shrink on Scroll, Expand on Top
Sriram HackerNoon profile picture


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 in React.

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. I am looking forward to your feedback; thank you!


Also published here.