Skip to content

StickyHeader

SM · 950B gzip budget

Header that pins to top on scroll with shrink animation.

Category
Navigation
Budget
sm, single-purpose control
External runtime
React / React DOM only
axe fixtureSSR rendersource scanbundle budget

Preview

Live fixture
Preview locale
Sticky

Try the component here. Locale-aware formatting and direction follow the selected language; example content and labels are not automatically translated. Overlay demos use interactive launchers.

Install

npx shadcn@latest add https://gear5-ui.vercel.app/r/sticky-header.json

Copies the source into your project.

New here? Set up Tailwind and import aliases first →

Source & props

Edit on GitHub ↗

Prop types and inline documentation are included below. This is repository source; the installer rewrites shared imports for your project.

"use client";

import React, { useEffect, useRef, useState } from "react";

export interface StickyHeaderProps {
  children: React.ReactNode;
  offset?: number;
  className?: string;
  shadowOnStuck?: boolean;
}

export function StickyHeader({
  children,
  offset = 0,
  className = "",
  shadowOnStuck = true,
}: StickyHeaderProps) {
  const [isStuck, setIsStuck] = useState(false);
  const sentinelRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const sentinel = sentinelRef.current;
    if (!sentinel) return;

    const observer = new IntersectionObserver(
      ([entry]) => {
        setIsStuck(!entry.isIntersecting);
      },
      { threshold: [1], rootMargin: `-${offset + 1}px 0px 0px 0px` }
    );

    observer.observe(sentinel);
    return () => observer.disconnect();
  }, [offset]);

  return (
    <>
      <div ref={sentinelRef} style={{ height: 0, visibility: "hidden" }} aria-hidden="true" />
      <header
        className={className}
        style={{
          position: "sticky",
          top: offset,
          zIndex: 10,
          transition: "box-shadow 0.2s ease",
          boxShadow: shadowOnStuck && isStuck
            ? "0 2px 8px rgba(0, 0, 0, 0.1)"
            : "none",
        }}
      >
        {children}
      </header>
    </>
  );
}

What CI checks

Quality contract
  • Bundled, minified and gzipped against its tier budget
  • Audited by axe in the state previewed above
  • Rendered through react-dom/server with no browser globals
  • Scanned for network calls, dangerous sinks, and unguarded animation