Progress indicators

Progress indicators show the status of a process in real time

Usage

Progress indicators inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates.

Instalation

Run the following command:

npm i @radix-ui/react-progress

Copy and paste the following code into your project.

'use client'

import * as React from 'react'
import * as ProgressPrimitive from '@radix-ui/react-progress'

import { cn } from '@/lib/utils'

export interface ProgressProps
  extends React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> {}

const LinearProgress = React.forwardRef<
  React.ElementRef<typeof ProgressPrimitive.Root>,
  ProgressProps
>(({ className, value, ...props }, ref) => {
  return (
    <ProgressPrimitive.Root
      ref={ref}
      className={cn(
        'relative h-1 w-full overflow-hidden bg-surfaceContainerHighest',
        className
      )}
      value={value}
      {...props}
    >
      <ProgressPrimitive.Indicator
        className={cn(
          'h-full w-full flex-1 bg-primary transition-transform data-[state=indeterminate]:animate-linear-progress'
        )}
        style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
      />
    </ProgressPrimitive.Root>
  )
})
LinearProgress.displayName = 'LinearProgress'

export interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
  value?: number | null
}

const CircularProgress = React.forwardRef<HTMLDivElement, ProgressProps>(
  ({ value = null, className }, ref) => {
    const state = React.useMemo(() => {
      if (typeof value !== 'number') return 'indeterminate'
      if (value === 100) return 'complete'
      return 'loading'
    }, [value])

    return (
      <span
        ref={ref}
        data-state={state}
        className={cn(
          'group inline-block aspect-square h-12 shrink-0 text-primary',
          className
        )}
      >
        <svg
          className="block h-full w-full -rotate-90 group-data-[state=indeterminate]:animate-progress-spin"
          viewBox="22 22 44 44"
        >
          <circle
            className="stroke-current transition-[stroke-dashoffset] duration-300 group-data-[state=indeterminate]:animate-circular-progress"
            cx="44"
            cy="44"
            r="14"
            fill="none"
            strokeWidth="4"
            {...(value && {
              style: {
                strokeDasharray: '100, 200',
                strokeDashoffset: 100 - value,
              },
            })}
          />
        </svg>
      </span>
    )
  }
)
CircularProgress.displayName = 'CircularProgress'

export { LinearProgress, CircularProgress }

Update the import paths to match your project setup.

Examples

Determinate Linear Progress

Linear progress indicators display progress by animating along the length of a fixed, visible track. The behavior of the indicator is dependent on whether the progress of a process is known.

Progress

import { LinearProgress } from '@/components/ui/progress-indicator'

export const IndeterminateLinearProgress = () => {
  return <LinearProgress value={13} />
}

Indeterminate Linear Progress

import { LinearProgress } from '@/components/ui/progress-indicator'

export const IndeterminateLinearProgress = () => {
  return <LinearProgress />
}

Determinate Circular Progress

Circular progress indicators display progress by animating along an invisible circular track in a clockwise direction.

Progress

import { CircularProgress } from '@/components/ui/progress-indicator'

export const DeterminateCircularProgress = () => {
  return <CircularProgress value={13} />
}

Indeterminate Curcular Progress

import { CircularProgress } from '@/components/ui/progress-indicator'

export const DeterminateCircularProgress = () => {
  return <CircularProgress />
}