🙆

【React】上スクロールした時のみ固定表示するヘッダーを実装する方法

2025/02/22に公開

検索機能がついているサイトでよくある、画面を上にスクロールすると検索窓のついたヘッダーが固定表示される機能の実装方法をまとめます。

※例えば、クックパッド(スマホ版)のレシピを表示する画面では画面を下にスクロールしている際は以下の画像のようにヘッダーには何も表示されません。

しかし、上にスクロールすると以下の画像のようにヘッダーに検索窓が固定表示されます。

結論

以下のようなコードになります。
※CSSはTailwind CSSを用いています。

import { useState, useEffect } from "react";

const Header = () => {
  const [isSticky, setIsSticky] = useState(false);
  const [lastScrollY, setLastScrollY] = useState(0);

  useEffect(() => {
    const handleScroll = () => {
      const currentScrollY = window.scrollY;

      if (currentScrollY < lastScrollY) {
        setIsSticky(true);
      } else {
        setIsSticky(false);
      }

      setLastScrollY(currentScrollY);
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [lastScrollY]);

  return (
    <header
      className={`transition-all ease-in-out duration-300 w-full ${
        isSticky
          ? "fixed top-0 left-0 bg-yellow-200 z-50"
          : "relative"
      } p-4`}
    >
      <h1 className={`text-xl ${isSticky ? "text-lg" : "text-2xl"} text-black`}>
        上にスクロールした時のみ固定表示
      </h1>
    </header>
  );
};

export default Header;

ポイントは上にスクロールされたことを検知し、上にスクロールされた場合はヘッダーに固定表示するためのスタイルを付与することです。

上にスクロールされたことを検知する方法

コードの以下の部分で検知しています。

useEffect(() => {
    const handleScroll = () => {
      const currentScrollY = window.scrollY;

      if (currentScrollY < lastScrollY) {
        setIsSticky(true);
      } else {
        setIsSticky(false);
      }

      setLastScrollY(currentScrollY);
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [lastScrollY]);

スクロールされる度に以下のような流れの処理が動きます。

  1. 直近のスクロール位置がlastScrollYに記録されている
  2. 現在のスクロール位置(currentScrollY)を取得
  3. 直近のスクロール位置と現在のスクロール位置を比較
  4. 現在のスクロール位置の方が上であれば、ヘッダーに固定表示するためのスタイルを付与

上に固定表示させるためのスタイル

Tailwind CSSで書くと以下です。
fixed top-0 left-0 bg-yellow-200 z-50

通常のCSSの形式に直すと以下です。

// 固定する位置を指定(必須)
position: fixed;
top: 0;
left: 0;
// 目立たせるために便宜上背景色を黄色にする(必須ではない)
background-color: rgb(254 240 138);
// 他の要素より上に配置(必須、値は他の要素のz-indexとの兼ね合いで調整する)
z-index: 50;

Discussion