Skip to content

Hide On Scroll Navbar

A navbar that slides away on scroll-down and reappears on scroll-up.

Freeminimaleditorial
Category
Patterns
Added
2026-08-24
Deps
1
Updated
2026-09-01
Preview
Brandscroll ↓

Section content line 1

Section content line 2

Section content line 3

Section content line 4

Section content line 5

Section content line 6

Section content line 7

Section content line 8

Section content line 9

Section content line 10

Section content line 11

Section content line 12

Section content line 13

Section content line 14

Props

This entry takes no configurable props.

Requires framer-motion
"use client";

import { useRef, useState } from "react";
import { motion, useMotionValueEvent, useScroll } from "framer-motion";

export function HideOnScrollNavbar({ children }: { children?: React.ReactNode }) {
  const { scrollY } = useScroll();
  const [hidden, setHidden] = useState(false);
  const [inert, setInert] = useState(false);
  const lastY = useRef(0);

  useMotionValueEvent(scrollY, "change", (y) => {
    const diff = y - lastY.current;
    if (Math.abs(diff) > 4) {
      const nextHidden = diff > 0 && y > 40;
      if (!nextHidden) setInert(false);
      setHidden(nextHidden);
      lastY.current = y;
    }
  });

  return (
    <motion.div
      animate={{ y: hidden ? "-100%" : "0%" }}
      transition={{ duration: 0.25, ease: [0.65, 0, 0.35, 1] }}
      onAnimationComplete={() => {
        if (hidden) setInert(true);
      }}
      inert={inert}
      className="fixed inset-x-0 top-0 z-40 flex items-center justify-between border-b border-neutral-200 bg-white/90 px-6 py-3 backdrop-blur-sm"
    >
      {children ?? <span className="font-bold">Brand</span>}
    </motion.div>
  );
}
Notes

About this pattern

The pattern is reclaiming vertical space on scroll without losing the nav entirely: hide it on the way down, bring it back the instant the user scrolls up, which is usually a sign they're hunting for something rather than just reading. Reach for it over a plain sticky navbar on long-form or content-dense pages where the header is competing for screen space; reach for a simpler shrink-on-scroll or fade-to-translucent treatment instead if hiding it outright would make navigation feel unreliable, like inside a checkout flow. Direction comes from diffing the current scrollY against a value held in a ref rather than state, inside a Framer Motion useMotionValueEvent listener — a ref avoids triggering a render on every scroll tick, and useScroll already batches internally to animation frames, so no extra throttling is needed on top. A 4px delta threshold absorbs jitter from trackpads and momentum scrolling so the bar doesn't flicker open and shut on tiny movements, and hidden only flips true past y > 40, so scrolling down from the very top never immediately hides it. One real gap between the two files: the shipped code tracks window scroll, exactly what a page header needs, while the preview scopes useScroll to its own container since it's a demo inside a scrollable card — don't carry that container-scoped version into a real header.

Curator’s note

The live preview scopes useScroll to its own scrollable card (useScroll({ container })) since this is an isolated demo, not the page; the shipped code above tracks window scroll instead, which is what a real site header needs.

Related

Pairs well with

Matched on shared tags and category — the entries most likely to be used alongside this one.

Cards that pin and stack on top of each other as you scroll past.

scrollstickystack
NewPro

A sticky section nav whose items fill like progress bars as you read — and still reaches the short last section that fixed-line navs never activate.

navigationscrollsticky
New

A pile of cards that fans out symmetrically as its section crosses the viewport.

scrollcardssticky