rapidUI

Progress

Horizontal progress bar built on Base UI Progress.

Installation

pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/progress.json

Installs to @/components/ui/progress.

Manual installation

Install the dependencies:

pnpm add @base-ui/react@^1.8.0

Copy the source into your project:

components/ui/progress.tsx
'use client'

import { Progress as ProgressPrimitive } from '@base-ui/react/progress'
import { cn } from '@/lib/utils'

function Progress({
	className,
	children,
	value = null,
	...props
}: Omit<ProgressPrimitive.Root.Props, 'value'> & {
	/** The current value (0–`max`). Leave it out or pass `null` for an indeterminate progress bar. */
	value?: number | null
}) {
	return (
		<ProgressPrimitive.Root
			value={value}
			data-slot="progress"
			className={cn('flex flex-wrap gap-3', className)}
			{...props}
		>
			{children}
			<ProgressTrack>
				<ProgressIndicator />
			</ProgressTrack>
		</ProgressPrimitive.Root>
	)
}

function ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {
	return (
		<ProgressPrimitive.Track
			className={cn('relative flex h-1 w-full items-center overflow-x-hidden rounded-full bg-secondary', className)}
			data-slot="progress-track"
			{...props}
		/>
	)
}

function ProgressIndicator({ className, ...props }: ProgressPrimitive.Indicator.Props) {
	return (
		<ProgressPrimitive.Indicator
			data-slot="progress-indicator"
			className={cn('h-full bg-primary transition-all', className)}
			{...props}
		/>
	)
}

function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
	return (
		<ProgressPrimitive.Label className={cn('text-sm font-medium', className)} data-slot="progress-label" {...props} />
	)
}

function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
	return (
		<ProgressPrimitive.Value
			className={cn('ml-auto text-sm text-muted-foreground tabular-nums', className)}
			data-slot="progress-value"
			{...props}
		/>
	)
}

export { Progress, ProgressTrack, ProgressIndicator, ProgressLabel, ProgressValue }

A progress component built on top of Base UI Progress, providing an accessible way to display progress or loading state.

Features

  • Built on the Base UI Progress primitive (@base-ui/react package)
  • Slim h-1 track on bg-secondary with a bg-primary indicator by default
  • Optional ProgressLabel and ProgressValue children, which label the bar and show the formatted value
  • Indeterminate when value is left out or null
  • Smooth transitions between values
  • Height and colors customizable via className
  • Screen reader support (role="progressbar")

Usage

Give every progress bar an accessible name with aria-label or aria-labelledby.

import { Progress, ProgressLabel, ProgressValue } from '@/components/ui/progress'

// Basic usage
export function BasicProgress() {
  return <Progress value={40} aria-label="Application progress" />
}

// With a visible label and value; ProgressLabel names the progress bar
export function LabelledProgress() {
  return (
    <Progress value={40}>
      <ProgressLabel>Applicant details</ProgressLabel>
      <ProgressValue />
    </Progress>
  )
}

// With custom height (default is h-1): className goes on the root, so target the track
export function CustomSizeProgress() {
  return <Progress value={60} aria-label="Upload progress" className="*:data-[slot=progress-track]:h-2" />
}

// With a custom indicator colour (use theme tokens)
export function CustomColorProgress() {
  return (
    <Progress value={80} aria-label="Places filled" className="**:data-[slot=progress-indicator]:bg-chart-2" />
  )
}

// Animated progress
export function AnimatedProgress() {
  const [progress, setProgress] = React.useState(0)

  React.useEffect(() => {
    const timer = setInterval(() => {
      setProgress((prev) => (prev >= 100 ? 0 : prev + 10))
    }, 500)
    return () => clearInterval(timer)
  }, [])

  return <Progress value={progress} aria-label="Archive transfer" />
}

Examples

Default

Progress through a five-step e-service, labelled with aria-labelledby.

With Animation

The value changes after a short delay and the indicator slides smoothly.

Different Sizes

The default h-1 track, then h-2 and h-3 via className="*:data-[slot=progress-track]:h-2".

Custom Colors

Preschool capacity with indicators coloured from the chart-1 to chart-4 tokens.

With Label

File uploads for a building permit, each with its own progress and status text.

Loading Animation

A long-running archive transfer with a continuous progress loop.

Props

Progress

  • value - The current progress value (0-100); leave it out or pass null for an indeterminate bar
  • min / max - The range (defaults 0 and 100)
  • className - Additional CSS classes (applied to the root, a wrapping flex container; target the track with *:data-[slot=progress-track]:… and the indicator with **:data-[slot=progress-indicator]:…)
  • format / locale - Intl.NumberFormat options and locale for the value text
  • getAriaValueText - (formattedValue, value) => string, the text read by screen readers
  • children - Rendered before the track, typically ProgressLabel and ProgressValue

ProgressLabel, ProgressValue, ProgressTrack, ProgressIndicator

  • ProgressLabel - A visible label that names the progress bar (aria-labelledby)
  • ProgressValue - The formatted value, e.g. 40%
  • ProgressTrack / ProgressIndicator - The parts Progress renders, exported for custom layouts

Accessibility

The Progress component follows WAI-ARIA guidelines:

  • Uses the progressbar role
  • Provides the value through aria-valuenow and aria-valuetext (a formatted percentage by default)
  • Includes aria-valuemin and aria-valuemax
  • Supports custom value text with getAriaValueText
  • Screen readers do not announce value changes on their own; use a status message (a polite live region) for milestones such as "Upload complete"
  • Every progress bar needs a name: aria-label, aria-labelledby or a ProgressLabel

Best Practices

  1. Values:

    • Use 0-100 range for consistency
    • Handle edge cases (null, undefined)
    • Consider decimal precision
    • Validate input values
  2. Visual Design:

    • Maintain sufficient contrast
    • Use appropriate sizes
    • Consider color meaning
    • Provide clear visual feedback
  3. Animation:

    • Use smooth transitions
    • Avoid excessive animations
    • Consider reduced motion
    • Handle interruptions
  4. Accessibility:

    • Provide meaningful labels
    • Test with screen readers
    • Consider color blindness
    • Maintain ARIA attributes
  5. Implementation:

    • Handle loading states
    • Consider performance
    • Test edge cases
    • Maintain responsiveness
  • Slider
  • Spinner
  • Skeleton