Skip to content

ProgressBar

A styled progress/scrub bar component for displaying and controlling playback position, loading progress, or other linear values.

Features

  • Visual fill gradient showing progress
  • Interactive scrubbing via hidden range input
  • Optional value display with custom formatting
  • Configurable min/max/step values
  • Disabled state support
  • Smooth visual transitions
  • Completely customizable via className props
  • Event propagation control (won't drag parent modules)

Import

tsx
import { ProgressBar } from '@mode-7/mod';

Props

PropTypeDefaultDescription
valuenumberRequiredCurrent value/position
onChange(value: number) => voidRequiredValue change callback
minnumber0Minimum value
maxnumber100Maximum value
stepnumber1Step increment
disabledbooleanfalseDisabled state
showValuebooleanfalseShow formatted value text
formatValue(value: number) => string-Custom value formatter
classNamestring''Additional CSS classes for container
barClassNamestring''Additional CSS classes for bar

Usage

Basic Usage

tsx
import { ProgressBar } from '@mode-7/mod';
import { useState } from 'react';

function BasicProgress() {
  const [value, setValue] = useState(50);

  return (
    <ProgressBar
      value={value}
      onChange={setValue}
      min={0}
      max={100}
    />
  );
}

Audio Playback Scrubber

tsx
function AudioScrubber({ currentTime, duration, onSeek }) {
  const formatTime = (seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, '0')}`;
  };

  return (
    <ProgressBar
      value={currentTime}
      onChange={onSeek}
      min={0}
      max={duration || 100}
      step={0.1}
      showValue={true}
      formatValue={() => `${formatTime(currentTime)} / ${formatTime(duration)}`}
    />
  );
}

Loading Progress

tsx
function LoadingIndicator({ progress }) {
  return (
    <ProgressBar
      value={progress}
      onChange={() => {}} // Read-only
      min={0}
      max={100}
      disabled={true}
      showValue={true}
      formatValue={(v) => `${v.toFixed(0)}%`}
    />
  );
}

Volume Level

tsx
function VolumeBar() {
  const [volume, setVolume] = useState(80);

  return (
    <ProgressBar
      value={volume}
      onChange={setVolume}
      min={0}
      max={100}
      showValue={true}
      formatValue={(v) => `${v}%`}
    />
  );
}

Buffer Progress

tsx
function BufferIndicator({ buffered, duration }) {
  const percentage = (buffered / duration) * 100;

  return (
    <ProgressBar
      value={percentage}
      onChange={() => {}}
      min={0}
      max={100}
      disabled={true}
      className="buffer-progress"
    />
  );
}

With Custom Step

tsx
<ProgressBar
  value={gain}
  onChange={setGain}
  min={0}
  max={2}
  step={0.01}
  showValue={true}
  formatValue={(v) => `${(v * 100).toFixed(0)}%`}
/>

Disabled State

tsx
<ProgressBar
  value={value}
  onChange={setValue}
  disabled={true}
/>

CSS Classes

The ProgressBar component uses the following CSS classes:

  • .modui-progressbar - Container element
  • .modui-progressbar-bar - Bar container
  • .modui-progressbar-fill - Fill gradient element
  • .modui-progressbar-input - Hidden range input
  • .modui-progressbar-value - Value display text
  • .modui-progressbar-disabled - Applied when disabled

Styling Examples

Custom Colors

css
.custom-progress .modui-progressbar-bar {
  background: #1a1a2e;
  border-color: #3949ab;
}

.custom-progress .modui-progressbar-fill {
  background: linear-gradient(90deg, #3949ab, #5c6bc0);
}

Compact Size

css
.compact-progress .modui-progressbar-bar {
  height: 4px;
  border-radius: 2px;
}

Thick Bar

css
.thick-progress .modui-progressbar-bar {
  height: 16px;
  border-radius: 8px;
}

.thick-progress .modui-progressbar-value {
  font-size: 0.875rem;
  margin-top: 0.5rem;
}

Success Color

css
.success-progress .modui-progressbar-fill {
  background: linear-gradient(90deg, #4CAF50, #66BB6A);
}

Warning Color

css
.warning-progress .modui-progressbar-fill {
  background: linear-gradient(90deg, #FFA726, #FFB74D);
}

Danger Color

css
.danger-progress .modui-progressbar-fill {
  background: linear-gradient(90deg, #EF5350, #E57373);
}

Common Patterns

MP3Deck Integration

tsx
import { MP3Deck, ProgressBar } from '@mode-7/mod';
import { useModStream } from '@mode-7/mod';

function MP3Player() {
  const output = useModStream();

  const formatTime = (seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, '0')}`;
  };

  return (
    <MP3Deck output={output}>
      {(controls) => (
        <div className="mp3-player">
          <ProgressBar
            value={controls.currentTime}
            onChange={(value) => controls.seek(value)}
            min={0}
            max={controls.duration || 100}
            step={0.1}
            disabled={!controls.src}
            showValue={true}
            formatValue={() =>
              controls.src
                ? `${formatTime(controls.currentTime)} / ${formatTime(controls.duration)}`
                : '0:00 / 0:00'
            }
          />
          {/* Other controls... */}
        </div>
      )}
    </MP3Deck>
  );
}

StreamingAudioDeck Integration

tsx
import { StreamingAudioDeck, ProgressBar } from '@mode-7/mod';
import { useModStream } from '@mode-7/mod';

function StreamPlayer() {
  const output = useModStream();

  const formatTime = (seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, '0')}`;
  };

  return (
    <StreamingAudioDeck output={output}>
      {(controls) => (
        <div className="stream-player">
          <ProgressBar
            value={controls.currentTime}
            onChange={(value) => controls.seek(value)}
            min={0}
            max={controls.duration || 100}
            step={0.1}
            disabled={!controls.url}
            showValue={true}
            formatValue={() =>
              controls.url
                ? `${formatTime(controls.currentTime)} / ${formatTime(controls.duration)}`
                : '0:00 / 0:00'
            }
          />
          {/* Other controls... */}
        </div>
      )}
    </StreamingAudioDeck>
  );
}

Read-Only Progress

tsx
function DownloadProgress({ progress }) {
  return (
    <ProgressBar
      value={progress}
      onChange={() => {}} // No-op for read-only
      min={0}
      max={100}
      disabled={true}
      showValue={true}
      formatValue={(v) => `Downloading... ${v.toFixed(0)}%`}
    />
  );
}

Multi-Range Display

tsx
function MultiProgress() {
  const [position, setPosition] = useState(0);
  const [buffer, setBuffer] = useState(0);

  return (
    <div style={{ position: 'relative' }}>
      {/* Buffer indicator (behind) */}
      <ProgressBar
        value={buffer}
        onChange={() => {}}
        disabled={true}
        className="buffer-bar"
      />
      {/* Playback position (on top) */}
      <ProgressBar
        value={position}
        onChange={setPosition}
        className="position-bar"
        style={{ position: 'absolute', top: 0, left: 0, right: 0 }}
      />
    </div>
  );
}

Time Formatting Helpers

Standard Time Format

tsx
const formatTime = (seconds: number): string => {
  const mins = Math.floor(seconds / 60);
  const secs = Math.floor(seconds % 60);
  return `${mins}:${secs.toString().padStart(2, '0')}`;
};

Hours:Minutes:Seconds

tsx
const formatTimeWithHours = (seconds: number): string => {
  const hrs = Math.floor(seconds / 3600);
  const mins = Math.floor((seconds % 3600) / 60);
  const secs = Math.floor(seconds % 60);

  if (hrs > 0) {
    return `${hrs}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  }
  return `${mins}:${secs.toString().padStart(2, '0')}`;
};

Accessibility

The ProgressBar component includes:

  • Native range input for keyboard support (arrow keys)
  • Disabled state handling
  • Focus styles
  • Proper value boundaries

Released under the MIT License.