Scroll Progress
Scroll progress indicator. Bar mode renders a fixed top bar; ring mode renders an animated SVG circle.
Use and inspectUse this when the motion supports the surrounding content. Connect callbacks and state to your app, keep the reset preview as a local demonstration, and add a reduced-motion fallback for movement or looping. The preview is synthetic; it does not call a backend.Next: inspect the source below and replace the demo inputs with your own data.
Scroll ProgressScroll progress indicator. Bar mode renders a fixed top bar; ring mode renders an animated SVG circle.
"use client";
import { useScroll, useSpring, useTransform, motion } from "framer-motion";
type ScrollProgressProps = {
type?: "bar" | "ring";
color?: string;
size?: number;
};
const RADIUS = 40;
const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
export function ScrollProgress({ type = "ring", color = "#7c3aed", size = 64 }: ScrollProgressProps) {
const { scrollYProgress } = useScroll();
const progress = useSpring(scrollYProgress, { stiffness: 100, damping: 30 });
const dashOffset = useTransform(progress, (p: number) => CIRCUMFERENCE * (1 - p));
if (type === "bar") {
return (
<div className="mp-scroll-progress-bar-wrap">
<motion.div
className="mp-scroll-progress-bar"
style={{ scaleX: progress, background: color }}
/>
</div>
);
}
return (
<svg width={size} height={size} viewBox="0 0 100 100" className="mp-scroll-progress-ring">
<circle cx="50" cy="50" r={RADIUS} className="mp-scroll-progress-track" />
<motion.circle
cx="50" cy="50" r={RADIUS}
strokeDasharray={CIRCUMFERENCE}
style={{ strokeDashoffset: dashOffset }}
stroke={color}
className="mp-scroll-progress-fill"
/>
</svg>
);
}Install
npm install framer-motionProps
| Prop | Type | Default | Description |
|---|---|---|---|
| type | "bar" | "ring" | "ring" | Visual style |
| color | string | "#7c3aed" | Progress fill color |
| size | number | 64 | Ring size in px (ring mode only) |