Scroll Progress
A scroll progress component that displays the progress of the scroll.
Powered by
Installation
npx shadcn@latest add @redpanda/scroll-progresspnpm dlx shadcn@latest add @redpanda/scroll-progressyarn dlx shadcn@latest add @redpanda/scroll-progressbunx shadcn@latest add @redpanda/scroll-progressnubx shadcn@latest add @redpanda/scroll-progressaube dlx shadcn@latest add @redpanda/scroll-progressVertical orientation
Pass orientation="vertical" to pin the progress bar to the left edge of a positioned ancestor (scaleY instead of scaleX). Useful for reading-progress indicators inside sidebars or detail panels.
When to use
Scroll Progress provides visual feedback about reading progress through content. Use this decision tree to determine when it’s appropriate:
Motion features
ScrollProgress includes its synchronous Motion features; no application-level LazyMotion provider is required. The progressProps HTMLMotionProps contract retains drag and layout support. Its scroll-driven scale composes with consumer Motion transforms and inherits an enclosing MotionConfig.
Usage
Global Page Scroll Progress
Track the scroll progress of the entire page:
import { ScrollProgress } from '@/components/redpanda-ui/scroll-progress';
// Add to your main layout or pagefunction App() { return ( <> <ScrollProgress /> <main> {/* Your page content */} <article className="max-w-4xl mx-auto px-6 py-12"> <h1>Long Article Title</h1> <p>Very long content that will require scrolling...</p> {/* More content */} </article> </main> </> );}Local Container Scroll Progress
Track scroll progress within a specific scrollable container:
<ScrollProgress className="h-[400px] w-full border rounded-lg p-6"> <div className="space-y-6"> <h2>Scrollable Content Section</h2> <p>This content will scroll within the container...</p> {Array.from({ length: 20 }).map((_, i) => ( <div key={i} className="p-4 border rounded"> <h3>Section {i + 1}</h3> <p>Content for section {i + 1} goes here. This demonstrates the local scroll progress tracking.</p> </div> ))} </div></ScrollProgress>Custom Styled Progress Bar
Customize the appearance of the progress bar:
<ScrollProgress progressProps={{ className: 'fixed z-50 top-0 inset-x-0 h-2 bg-gradient-to-r from-primary to-brand origin-left', style: { boxShadow: '0 0 10px rgba(59, 130, 246, 0.5)', }, }}> <div className="h-[200vh] p-6"> <h1>Custom Styled Progress</h1> <p>Long content with a custom gradient progress bar...</p> </div></ScrollProgress>Reading Progress for Articles
Ideal for blog posts and articles:
function BlogPost() { return ( <> <ScrollProgress progressProps={{ className: 'fixed z-50 top-0 inset-x-0 h-1 bg-primary origin-left transition-opacity duration-300', }} /> <article className="max-w-2xl mx-auto px-4 py-16"> <header className="mb-12"> <h1 className="text-4xl font-bold mb-4">How to Build Better UIs</h1> <p className="text-subtle">A comprehensive guide...</p> </header>
{/* Long article content */} <p className="text-body-lg">Your article content goes here...</p> </article> </> );}Multiple Progress Indicators
Use different progress bars for different sections:
<div className="space-y-8"> <ScrollProgress className="h-[300px] border rounded-lg p-4" progressProps={{ className: 'absolute top-0 inset-x-0 h-1 bg-informative origin-left', }} > <h3>Section 1</h3> <div className="space-y-4">{/* Content for section 1 */}</div> </ScrollProgress>
<ScrollProgress className="h-[300px] border rounded-lg p-4" progressProps={{ className: 'absolute top-0 inset-x-0 h-1 bg-success origin-left', }} > <h3>Section 2</h3> <div className="space-y-4">{/* Content for section 2 */}</div> </ScrollProgress></div>Anatomy
The Scroll Progress component uses Motion/Framer Motion to create smooth scroll-tracking animations:
<ScrollProgress> Component├── <motion.div> Progress Bar│ ├── Fixed Positioning (global) | Absolute (local)│ ├── scaleX Animation - horizontal progress indicator│ ├── useSpring Hook - smooth animation transitions│ │ ├── stiffness: 250 - animation responsiveness│ │ ├── damping: 40 - bounce control│ │ └── bounce: 0 - no overshoot│ └── Visual Styling│ ├── height: h-1 (4px) default│ ├── background: bg-primary│ ├── origin: origin-left (scales from left)│ └── z-index: z-50 (above other content)├── Scroll Detection│ ├── useScroll Hook - tracks scroll position│ ├── scrollYProgress - normalized 0-1 value│ └── Container targeting (global vs local)└── Content Container (local mode only) ├── Scrollable wrapper - overflow-y-auto ├── Height management - h-full default └── Content rendering area
Animation Flow:├── Scroll Event → useScroll → scrollYProgress (0-1)├── useSpring → smooth value transformation├── scaleX style → visual progress bar width└── Real-time updates as user scrolls
Positioning Modes:├── Global Mode (no children)│ ├── Tracks document scroll│ ├── Fixed positioning│ └── Full viewport width└── Local Mode (with children) ├── Tracks container scroll ├── Absolute/relative positioning └── Container-specific widthKey Features:
- Smooth Animation: Spring-based physics for natural motion
- Dual Mode: Global page tracking or local container tracking
- Customizable Styling: Full control over appearance and positioning
- Performance Optimized: Uses requestAnimationFrame for smooth updates
- Responsive: Adapts to container width automatically
- Zero Configuration: Works out of the box with sensible defaults
Props
progressProps?HTMLMotionProps<"div">
The props for the progress element
HTMLMotionProps<"div">className?string
The class name for the scroll progress
stringchildren?React.ReactNode
The children for the scroll progress (if not provided, the progress will be global)
React.ReactNodeCredits
- We use Animate UI from imskyleen for all the animations.