Scroll Progress Bar

Reading progress indicator that fills as you scroll — fixed at the top of the viewport with smooth spring physics

scrollprogressindicatorframer-motionreading
Install dependencies
$npm install framer-motion
Preview

Source Code

"use client";

import { RefObject } from "react";
import { motion, useScroll, useSpring } from "framer-motion";

interface ScrollProgressBarProps {
  position?: "top" | "bottom";
  height?: number;
  color?: string;
  target?: RefObject<HTMLElement | null>;
  className?: string;
}

export default function ScrollProgressBar({
  position = "top",
  height = 4,
  color = "#6366f1",
  target,
  className = "",
}: ScrollProgressBarProps) {
  const { scrollYProgress } = useScroll(
    target ? { container: target as RefObject<HTMLElement> } : undefined
  );

  const scaleX = useSpring(scrollYProgress, {
    stiffness: 100,
    damping: 30,
    restDelta: 0.001,
  });

  const positionStyle =
    position === "top" ? { top: 0 } : { bottom: 0 };

  return (
    <motion.div
      style={{
        scaleX,
        originX: 0,
        height,
        backgroundColor: color,
        ...positionStyle,
      }}
      className={`pointer-events-none fixed left-0 right-0 z-50 ${className}`}
      aria-hidden="true"
    />
  );
}