Scroll Progress

Motion

Free, copy-pasteable Tailwind CSS & Framer Motion Scroll Progress component. Accessible, fully responsive, dark-mode ready, and customizable.

Keep readers oriented inside long articles or configuration processes using the Scroll Progress Indicator. This component maps scroll depth dynamically, animating a top page progress bar or a circular percentage ring using Framer Motion's high-efficiency useScroll hook.

Commonly placed at the very top of documentation articles, blog templates, or pinned in a dashboard viewport.

Implementation

"use client";

import React, { useRef } from "react";
import { motion, useScroll, useSpring } from "framer-motion";
import { cn } from "@/lib/utils";

interface ScrollProgressProps {
  className?: string;
  variant?: "bar" | "circle";
  targetRef?: React.RefObject<HTMLElement>;
  color?: string;
  height?: number;
}

export const ScrollProgress: React.FC<ScrollProgressProps> = ({
  className = "",
  variant = "bar",
  targetRef,
  color = "bg-gradient-to-r from-[#FF3903] via-[#FF6107] to-[#FF8A3D]",
  height = 3,
}) => {
  const { scrollYProgress } = useScroll(targetRef ? { container: targetRef } : undefined);

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

  const pathLength = useSpring(scrollYProgress, {
    stiffness: 120,
    damping: 24,
    restDelta: 0.001,
  });

  if (variant === "circle") {
    return (
      <div className={cn("h-10 w-10 z-50", className)}>
        <svg className="h-full w-full -rotate-90" viewBox="0 0 100 100">
          <circle
            cx="50"
            cy="50"
            r="30"
            className="stroke-stone-200 dark:stroke-stone-850 fill-none"
            strokeWidth="8"
          />
          <motion.circle
            cx="50"
            cy="50"
            r="30"
            className="stroke-[#FF3903] fill-none"
            strokeWidth="8"
            strokeDasharray="0 1"
            style={{ pathLength }}
            strokeLinecap="round"
          />
        </svg>
      </div>
    );
  }

  return (
    <motion.div
      className={cn("fixed top-0 left-0 right-0 z-50 origin-left", color, className)}
      style={{ scaleX, height }}
    />
  );
};

Usage

Container Bound Previews

Scroll inside the left container below to watch the horizontal gradient bar at the top of the container card and the circular tracker in the right block update smoothly in real time.

Bar Style (Container Bound)
1. Core Design System

A unified grid layout, standardized spacing values, and strict tokenization form the baseline for building interfaces that scale across multi-functional development teams.

2. Framer Motion Integration

By leveraging dynamic motion hooks, components transition smoothly. Hardware-accelerated renders bypass React state bottlenecks for continuous 60fps interaction.

3. Responsive Scaling

Every element automatically adjusts to layout limits. Spacing, padding, and text dimensions adapt dynamically from mobile viewports to desktop displays.

Circular Style (Container Bound)

SCROLL THE LEFT PANEL TO ROTATE

import React, { useRef } from "react";
import { ScrollProgress } from "@/components/motion/ScrollProgress";

const MyDocPage = () => {
  const containerRef = useRef(null);

  return (
    <div ref={containerRef} className="h-64 overflow-y-auto relative">
      {/* Top Progress Bar */}
      <ScrollProgress
        variant="bar"
        targetRef={containerRef}
        className="absolute top-0"
      />
      
      {/* Circle Corner Progress */}
      <ScrollProgress
        variant="circle"
        targetRef={containerRef}
        className="absolute bottom-4 right-4"
      />
      
      <div>...Long Scrollable Article Content...</div>
    </div>
  );
};

Props

PropTypeDefaultDescription
variant"bar" | "circle""bar"Visual styling representation (top horizontal line or corner progress dial)
targetRefRefObject<HTMLElement>Target scrollable container. Tracks window scroll if undefined
colorstring"bg-gradient-to-r from-[#FF3903] via-[#FF6107] to-[#FF8A3D]"Custom tailwind color class or CSS gradient for the bar fill
heightnumber3Height thickness of the horizontal progress line in pixels
classNamestring""Additional CSS custom classes