---
title: Scroll Progress
description: A scroll progress component that displays the progress of the scroll.
icon: activity
author:
  name: imskyleen
  url: https://github.com/imskyleen
mode: wide
---

<PoweredBy
  packages={[
    { name: 'Animate UI', url: 'https://animate-ui.com/docs/components/scroll-progress' },
    { name: 'Motion', url: 'https://motion.dev/' },
  ]}
/>

### Installation

```package-install
npx shadcn@latest add @redpanda/scroll-progress
```

<ComponentPreview name="scroll-progress-demo" />

## 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.

<ComponentPreview name="scroll-progress-vertical-demo" />

## When to use

Scroll Progress provides visual feedback about reading progress through content. Use this decision tree to determine when it's appropriate:

```mermaid
graph TD;
    A[❓ Long scrollable content?] --> B{Need progress indication?};
    A --> C[ℹ️ Skip progress indicator];
    B --> D{Reading/article context?};
    B --> E{General scrolling?};
    D --> F[✅ Use ScrollProgress];
    E --> G{Page-level scrolling?};
    E --> H{Component-level scrolling?};
    G --> I[✅ Global ScrollProgress];
    H --> J[✅ Local ScrollProgress with container];
    F --> K[✅ Consider fixed position];
    I --> L[✅ Fixed at top of viewport];
    J --> M[✅ Relative to component];

    classDef recommended fill:#4ade80,stroke:#22c55e,stroke-width:2px,color:#000;
    classDef discouraged fill:#fbbf24,stroke:#f59e0b,stroke-width:2px,color:#000;
    classDef decision fill:#60a5fa,stroke:#3b82f6,stroke-width:2px,color:#fff;
    classDef neutral fill:#9ca3af,stroke:#6b7280,stroke-width:2px,color:#fff;

    class F,I,J,K,L,M recommended;
    class A,B,D,E,G,H decision;
    class C neutral;
```

## 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:

```tsx
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:

```tsx
<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:

```tsx
<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:

```tsx
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:

```tsx
<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 width
```

### Key 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 | Default | Description |
| - | - | - | - |
| `progressProps?` | `HTMLMotionProps<"div">` | - | The props for the progress element |
| `className?` | `string` | - | The class name for the scroll progress |
| `children?` | `React.ReactNode` | - | The children for the scroll progress (if not provided, the progress will be global) |

## Credits

- We use [Animate UI](https://github.com/animate-ui/animate-ui) from [imskyleen](https://github.com/imskyleen) for all the animations.
