React - Top Nav Hide on Scroll

Overview

Estimated time: 15–25 minutes

Implement a top navigation bar that hides on scroll down and reappears on scroll up, with minimal reflows.

Try it: Hide on scroll

View source
function useScrollDir(){
  const [dir, setDir] = React.useState('up');
  const last = React.useRef(window.scrollY);
  React.useEffect(() => {
    let ticking = false;
    function onScroll(){
      if (!ticking){
        window.requestAnimationFrame(() => {
          const y = window.scrollY;
          const d = y > last.current ? 'down' : 'up';
          if (Math.abs(y - last.current) > 4) setDir(d);
          last.current = y;
          ticking = false;
        });
        ticking = true;
      }
    }
    window.addEventListener('scroll', onScroll, {passive:true});
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return dir;
}
function App(){
  const dir = useScrollDir();
  return (
    <div>
      <header style={{position:'sticky', top:0, transition:'transform .2s', transform: dir==='down'? 'translateY(-100%)' : 'translateY(0)', background:'var(--bg)', borderBottom:'1px solid var(--border)', padding:'8px 12px', zIndex:10}}>
        <strong>My App</strong> <span style={{opacity:.6}}>Top bar hides on scroll</span>
      </header>
      <main style={{height:480, overflow:'auto'}}>
        {[...Array(20)].map((_,i)=> <p key={i}>Content line {i+1} lorem ipsum dolor sit amet...</p>)}
      </main>
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('try-topnav')).render(<App />);

Syntax primer

  • Use requestAnimationFrame to throttle scroll handling.
  • Position: sticky keeps the header in flow but pinned to the top.

Common pitfalls

  • Using heavy onScroll logic—prefer throttling or rAF to avoid jank.

Exercises

  1. Only hide after a threshold (e.g., 64px scrolled) and reveal near top.