DEV Community

William Baker
William Baker

Posted on

Getting Started with React Headroom: Building Auto-Hiding Navigation

React Headroom is a lightweight library that automatically hides and shows navigation headers based on scroll direction. When users scroll down, the header hides to maximize content space, and when they scroll up, it reappears for easy navigation access. This guide walks through setting up and creating auto-hiding navigation using React Headroom with React, from installation to a working implementation. This is part 46 of a series on using React Headroom with React.

Prerequisites

Before you begin, make sure you have:

  • Node.js version 14.0 or higher installed
  • npm, yarn, or pnpm package manager
  • A React project (version 16.8 or higher) or create-react-app setup
  • Basic knowledge of React components
  • Familiarity with JavaScript/TypeScript

Installation

Install React Headroom using your preferred package manager:

npm install react-headroom
Enter fullscreen mode Exit fullscreen mode

Or with yarn:

yarn add react-headroom
Enter fullscreen mode Exit fullscreen mode

Or with pnpm:

pnpm add react-headroom
Enter fullscreen mode Exit fullscreen mode

After installation, your package.json should include:

{
  "dependencies": {
    "react-headroom": "^3.1.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Project Setup

React Headroom requires minimal setup. Import the component and you're ready to use it.

First Example / Basic Usage

Let's create a simple auto-hiding header. Create a new file src/HeadroomExample.jsx:

// src/HeadroomExample.jsx
import React from 'react';
import Headroom from 'react-headroom';

function HeadroomExample() {
  return (
    <div>
      <Headroom>
        <header style={{
          backgroundColor: '#2c3e50',
          color: 'white',
          padding: '15px 30px',
          boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
        }}>
          <h1 style={{ margin: 0, fontSize: '24px' }}>My Website</h1>
        </header>
      </Headroom>
      <div style={{ padding: '40px', minHeight: '200vh' }}>
        <h2>Scroll down to see the header hide</h2>
        <p>Scroll up to see it reappear.</p>
        <div style={{ height: '1500px', backgroundColor: '#f0f0f0', padding: '20px' }}>
          <p>This is a long content area to demonstrate the headroom effect.</p>
          <p>Keep scrolling down...</p>
          <p>And scrolling...</p>
          <p>The header will hide when you scroll down.</p>
          <p>Scroll back up to see it appear again.</p>
        </div>
      </div>
    </div>
  );
}

export default HeadroomExample;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

// src/App.jsx
import React from 'react';
import HeadroomExample from './HeadroomExample';
import './App.css';

function App() {
  return (
    <div className="App">
      <HeadroomExample />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This creates a header that automatically hides when scrolling down and reappears when scrolling up.

Understanding the Basics

React Headroom provides several key features:

  • Headroom component: Wraps your header to enable auto-hide functionality
  • Automatic detection: Detects scroll direction and hides/shows accordingly
  • Smooth animations: Smooth transitions when hiding and showing
  • Customization: Configurable thresholds and animation options
  • Zero configuration: Works out of the box with sensible defaults

Key concepts:

  • Scroll Detection: Headroom monitors scroll events to determine direction
  • Hide on Scroll Down: Header hides when user scrolls down
  • Show on Scroll Up: Header reappears when user scrolls up
  • Smooth Transitions: Uses CSS transitions for smooth animations
  • Threshold: Configurable scroll distance before hiding/showing

Here's an example with a navigation menu:

// src/NavigationHeadroom.jsx
import React from 'react';
import Headroom from 'react-headroom';

function NavigationHeadroom() {
  const navItems = ['Home', 'About', 'Services', 'Contact'];

  return (
    <div>
      <Headroom>
        <nav style={{
          backgroundColor: '#007bff',
          color: 'white',
          padding: '15px 30px',
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
          boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
        }}>
          <div style={{ fontSize: '20px', fontWeight: 'bold' }}>
            Logo
          </div>
          <div style={{ display: 'flex', gap: '20px' }}>
            {navItems.map(item => (
              <a
                key={item}
                href={`#${item.toLowerCase()}`}
                style={{
                  color: 'white',
                  textDecoration: 'none',
                  padding: '5px 10px',
                  borderRadius: '4px',
                  transition: 'background-color 0.3s'
                }}
                onMouseEnter={(e) => e.target.style.backgroundColor = 'rgba(255,255,255,0.2)'}
                onMouseLeave={(e) => e.target.style.backgroundColor = 'transparent'}
              >
                {item}
              </a>
            ))}
          </div>
        </nav>
      </Headroom>
      <div style={{ padding: '40px', minHeight: '200vh' }}>
        <h2>Navigation with Headroom</h2>
        <p>Scroll down to see the navigation hide.</p>
        <div style={{ height: '1500px', backgroundColor: '#f8f9fa', padding: '20px' }}>
          <p>Long content area...</p>
        </div>
      </div>
    </div>
  );
}

export default NavigationHeadroom;
Enter fullscreen mode Exit fullscreen mode

Practical Example / Building Something Real

Let's build a complete website layout with an auto-hiding header:

// src/WebsiteWithHeadroom.jsx
import React from 'react';
import Headroom from 'react-headroom';

function WebsiteWithHeadroom() {
  const menuItems = [
    { id: 1, label: 'Home', href: '#home' },
    { id: 2, label: 'About', href: '#about' },
    { id: 3, label: 'Services', href: '#services' },
    { id: 4, label: 'Portfolio', href: '#portfolio' },
    { id: 5, label: 'Contact', href: '#contact' }
  ];

  return (
    <div>
      <Headroom
        style={{
          zIndex: 1000
        }}
        pinStart={64}
        upTolerance={10}
        downTolerance={10}
      >
        <header style={{
          backgroundColor: '#ffffff',
          boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
          padding: '15px 30px',
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center'
        }}>
          <div style={{
            fontSize: '24px',
            fontWeight: 'bold',
            color: '#2c3e50'
          }}>
            MyApp
          </div>
          <nav style={{ display: 'flex', gap: '30px', alignItems: 'center' }}>
            {menuItems.map(item => (
              <a
                key={item.id}
                href={item.href}
                style={{
                  color: '#2c3e50',
                  textDecoration: 'none',
                  padding: '8px 15px',
                  borderRadius: '4px',
                  transition: 'all 0.3s',
                  fontWeight: '500'
                }}
                onMouseEnter={(e) => {
                  e.target.style.backgroundColor = '#f8f9fa';
                  e.target.style.color = '#007bff';
                }}
                onMouseLeave={(e) => {
                  e.target.style.backgroundColor = 'transparent';
                  e.target.style.color = '#2c3e50';
                }}
              >
                {item.label}
              </a>
            ))}
            <button
              style={{
                padding: '10px 20px',
                backgroundColor: '#007bff',
                color: 'white',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer',
                fontWeight: '500',
                transition: 'background-color 0.3s'
              }}
              onMouseEnter={(e) => e.target.style.backgroundColor = '#0056b3'}
              onMouseLeave={(e) => e.target.style.backgroundColor = '#007bff'}
            >
              Get Started
            </button>
          </nav>
        </header>
      </Headroom>

      <main>
        <section id="home" style={{
          padding: '100px 40px',
          textAlign: 'center',
          backgroundColor: '#f8f9fa',
          minHeight: '100vh'
        }}>
          <h1 style={{ fontSize: '48px', marginBottom: '20px' }}>Welcome to MyApp</h1>
          <p style={{ fontSize: '20px', color: '#666', marginBottom: '30px' }}>
            Scroll down to see the header automatically hide
          </p>
        </section>

        <section id="about" style={{
          padding: '80px 40px',
          backgroundColor: 'white',
          minHeight: '100vh'
        }}>
          <h2 style={{ fontSize: '36px', marginBottom: '20px' }}>About Us</h2>
          <p style={{ fontSize: '18px', lineHeight: '1.6', color: '#666' }}>
            This is the about section. The header will hide when you scroll down
            and reappear when you scroll up, providing a better user experience
            by maximizing screen space while keeping navigation accessible.
          </p>
        </section>

        <section id="services" style={{
          padding: '80px 40px',
          backgroundColor: '#f8f9fa',
          minHeight: '100vh'
        }}>
          <h2 style={{ fontSize: '36px', marginBottom: '20px' }}>Our Services</h2>
          <p style={{ fontSize: '18px', lineHeight: '1.6', color: '#666' }}>
            Service content goes here. Keep scrolling to see the headroom effect in action.
          </p>
        </section>

        <section id="portfolio" style={{
          padding: '80px 40px',
          backgroundColor: 'white',
          minHeight: '100vh'
        }}>
          <h2 style={{ fontSize: '36px', marginBottom: '20px' }}>Portfolio</h2>
          <p style={{ fontSize: '18px', lineHeight: '1.6', color: '#666' }}>
            Portfolio content goes here.
          </p>
        </section>

        <section id="contact" style={{
          padding: '80px 40px',
          backgroundColor: '#f8f9fa',
          minHeight: '100vh'
        }}>
          <h2 style={{ fontSize: '36px', marginBottom: '20px' }}>Contact Us</h2>
          <p style={{ fontSize: '18px', lineHeight: '1.6', color: '#666' }}>
            Contact information goes here.
          </p>
        </section>
      </main>
    </div>
  );
}

export default WebsiteWithHeadroom;
Enter fullscreen mode Exit fullscreen mode

Now create a blog layout with headroom:

// src/BlogWithHeadroom.jsx
import React from 'react';
import Headroom from 'react-headroom';

function BlogWithHeadroom() {
  const blogPosts = Array.from({ length: 10 }, (_, i) => ({
    id: i + 1,
    title: `Blog Post ${i + 1}`,
    excerpt: `This is the excerpt for blog post ${i + 1}. It contains some sample text to demonstrate the headroom effect.`
  }));

  return (
    <div>
      <Headroom
        style={{
          zIndex: 1000
        }}
      >
        <header style={{
          backgroundColor: '#2c3e50',
          color: 'white',
          padding: '15px 30px',
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
          boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
        }}>
          <div style={{ fontSize: '24px', fontWeight: 'bold' }}>
            My Blog
          </div>
          <nav style={{ display: 'flex', gap: '20px' }}>
            <a href="#home" style={{ color: 'white', textDecoration: 'none' }}>Home</a>
            <a href="#posts" style={{ color: 'white', textDecoration: 'none' }}>Posts</a>
            <a href="#about" style={{ color: 'white', textDecoration: 'none' }}>About</a>
          </nav>
        </header>
      </Headroom>

      <main style={{ padding: '40px', maxWidth: '800px', margin: '0 auto' }}>
        <h1 style={{ marginBottom: '30px' }}>Blog Posts</h1>
        {blogPosts.map(post => (
          <article
            key={post.id}
            style={{
              marginBottom: '40px',
              padding: '20px',
              backgroundColor: '#f8f9fa',
              borderRadius: '8px',
              boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
            }}
          >
            <h2 style={{ marginBottom: '10px' }}>{post.title}</h2>
            <p style={{ color: '#666', lineHeight: '1.6' }}>{post.excerpt}</p>
          </article>
        ))}
      </main>
    </div>
  );
}

export default BlogWithHeadroom;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

// src/App.jsx
import React from 'react';
import WebsiteWithHeadroom from './WebsiteWithHeadroom';
import './App.css';

function App() {
  return (
    <div className="App">
      <WebsiteWithHeadroom />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This example demonstrates:

  • Auto-hiding navigation header
  • Smooth hide/show animations
  • Customizable scroll thresholds
  • Full website layout
  • Blog layout with headroom
  • Responsive navigation

Common Issues / Troubleshooting

  1. Header not hiding: Make sure the content is scrollable. If the page content is shorter than the viewport, there's nothing to scroll and the header won't hide. Add enough content to enable scrolling.

  2. Header hiding too quickly: Adjust the downTolerance prop to increase the scroll distance before hiding. Higher values require more scrolling before the header hides.

  3. Header not reappearing: Check the upTolerance prop. This controls how much you need to scroll up before the header reappears. Lower values make it reappear sooner.

  4. Z-index issues: If the header appears behind other elements, set a higher zIndex in the Headroom component's style prop.

  5. Animation not smooth: React Headroom uses CSS transitions by default. If animations seem choppy, check for CSS conflicts or performance issues.

  6. Header not pinning at top: Use the pinStart prop to control when the header should start pinning. This is useful if you have content above the header that should scroll normally first.

Next Steps

Now that you have a basic understanding of React Headroom:

  • Learn about advanced configuration options
  • Explore different animation styles
  • Implement custom scroll thresholds
  • Add header state callbacks
  • Integrate with React Router for navigation
  • Learn about other sticky header libraries
  • Check the official repository: https://github.com/KyleAMathews/react-headroom
  • Look for part 47 of this series for more advanced topics

Summary

You've successfully set up React Headroom in your React application and created auto-hiding navigation headers that improve user experience by maximizing content space while keeping navigation accessible. React Headroom provides a simple solution for creating headers that automatically hide and show based on scroll direction.

SEO Keywords

react-headroom
React auto-hiding header
react-headroom tutorial
React sticky navigation
react-headroom installation
React scroll header
react-headroom example
React hide on scroll
react-headroom setup
React navigation header
react-headroom customization
React scroll detection
react-headroom animations
React header library
react-headroom getting started

Top comments (0)