Scroll Progress
A scroll progress component that displays the progress of the scroll.
Made by imskyleenPowered by
Installation
Vertical 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:
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 page
function 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-blue-500 to-purple-500 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-muted-foreground">A comprehensive guide...</p>
</header>
{/* Long article content */}
<Text variant="large">Your article content goes here...</Text>
</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
Prop
Type
Credits
- We use Animate UI from imskyleen for all the animations.
Recent changes
- patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
- minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
- minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
- majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
- minorv0.3.0Add theme-provider component to the registry with documentation and tests. Includes playground type improvements (export RegistryItem, remove as-const boilerplate) and docs site dark mode border color fix.#109