useProgressBar
Provides the accessibility implementation for a progress bar component. Progress bars show either determinate or indeterminate progress of an operation over time.
| install | yarn add react-aria | 
|---|---|
| version | 3.39.0 | 
| usage | import {useProgressBar} from 'react-aria' | 
API#
useProgressBar(
  (props: AriaProgressBarProps
)): ProgressBarAria
Features#
The <progress>
HTML element can be used to build a progress bar, however it is
very difficult to style cross browser. useProgressBar helps achieve accessible
progress bars and spinners that can be styled as needed.
- Exposed to assistive technology as a progress bar via ARIA
- Labeling support for accessibility
- Internationalized number formatting as a percentage or value
- Determinate and indeterminate progress support
Anatomy#
Progress bars consist of a track element showing the full progress of an operation, a fill element showing the current progress, a label, and an optional value label. The track and bar elements represent the progress visually, while a wrapper element represents the progress to assistive technology using the progressbar ARIA role.
useProgressBar returns two sets of props that you should spread onto the appropriate element:
| Name | Type | Description | 
| progressBarProps | DOMAttributes | Props for the progress bar container element. | 
| labelProps | DOMAttributes | Props for the progress bar's visual label element (if any). | 
If there is no visual label, an aria-label or aria-labelledby prop must be passed instead
to identify the element to screen readers.
Example#
import {useProgressBar} from 'react-aria';
function ProgressBar(props) {
  let {
    label,
    showValueLabel = !!label,
    value,
    minValue = 0,
    maxValue = 100
  } = props;
  let {
    progressBarProps,
    labelProps
  } = useProgressBar(props);
  // Calculate the width of the progress bar as a percentage
  let percentage = (value - minValue) / (maxValue - minValue);
  let barWidth = `%`;
  return (
    <div {...progressBarProps} style={{ width: 200 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        {label &&
          (
            <span {...labelProps}>
              {label}
            </span>
          )}
        {showValueLabel &&
          (
            <span>
              {progressBarProps['aria-valuetext']}
            </span>
          )}
      </div>
      <div style={{ height: 10, background: 'lightgray' }}>
        <div style={{ width: barWidth, height: 10, background: 'orange' }} />
      </div>
    </div>
  );
}
<ProgressBar label="Loading…" value={80} />import {useProgressBar} from 'react-aria';
function ProgressBar(props) {
  let {
    label,
    showValueLabel = !!label,
    value,
    minValue = 0,
    maxValue = 100
  } = props;
  let {
    progressBarProps,
    labelProps
  } = useProgressBar(props);
  // Calculate the width of the progress bar as a percentage
  let percentage = (value - minValue) /
    (maxValue - minValue);
  let barWidth = `%`;
  return (
    <div {...progressBarProps} style={{ width: 200 }}>
      <div
        style={{
          display: 'flex',
          justifyContent: 'space-between'
        }}
      >
        {label &&
          (
            <span {...labelProps}>
              {label}
            </span>
          )}
        {showValueLabel &&
          (
            <span>
              {progressBarProps['aria-valuetext']}
            </span>
          )}
      </div>
      <div style={{ height: 10, background: 'lightgray' }}>
        <div
          style={{
            width: barWidth,
            height: 10,
            background: 'orange'
          }}
        />
      </div>
    </div>
  );
}
<ProgressBar label="Loading…" value={80} />import {useProgressBar} from 'react-aria';
function ProgressBar(
  props
) {
  let {
    label,
    showValueLabel =
      !!label,
    value,
    minValue = 0,
    maxValue = 100
  } = props;
  let {
    progressBarProps,
    labelProps
  } = useProgressBar(
    props
  );
  // Calculate the width of the progress bar as a percentage
  let percentage =
    (value - minValue) /
    (maxValue -
      minValue);
  let barWidth = `%`;
  return (
    <div
      {...progressBarProps}
      style={{
        width: 200
      }}
    >
      <div
        style={{
          display:
            'flex',
          justifyContent:
            'space-between'
        }}
      >
        {label &&
          (
            <span
              {...labelProps}
            >
              {label}
            </span>
          )}
        {showValueLabel &&
          (
            <span>
              {progressBarProps[
                'aria-valuetext'
              ]}
            </span>
          )}
      </div>
      <div
        style={{
          height: 10,
          background:
            'lightgray'
        }}
      >
        <div
          style={{
            width:
              barWidth,
            height: 10,
            background:
              'orange'
          }}
        />
      </div>
    </div>
  );
}
<ProgressBar
  label="Loading…"
  value={80}
/>Circular example#
Progress bars may also be represented using a circular visualization rather than a line. This is often used to represent indeterminate operations, but may also be used for determinate progress indicators when space is limited. The following example shows a progress bar visualized as a circular spinner using SVG.
function ProgressCircle(props) {
  let { isIndeterminate, value, minValue = 0, maxValue = 100 } = props;
  let { progressBarProps } = useProgressBar(props);
  let center = 16;
  let strokeWidth = 4;
  let r = 16 - strokeWidth;
  let c = 2 * r * Math.PI;
  let percentage = isIndeterminate
    ? 0.25
    : (value - minValue) / (maxValue - minValue);
  let offset = c - percentage * c;
  return (
    <svg
      {...progressBarProps}
      width={32}
      height={32}
      viewBox="0 0 32 32"
      fill="none"
      strokeWidth={strokeWidth}
    >
      <circle
        role="presentation"
        cx={center}
        cy={center}
        r={r}
        stroke="gray"
      />
      <circle
        role="presentation"
        cx={center}
        cy={center}
        r={r}
        stroke="orange"
        strokeDasharray={` `}
        strokeDashoffset={offset}
        transform="rotate(-90 16 16)"
      >
        {props.isIndeterminate &&
          (
            <animateTransform
              attributeName="transform"
              type="rotate"
              begin="0s"
              dur="1s"
              from="0 16 16"
              to="360 16 16"
              repeatCount="indefinite"
            />
          )}
      </circle>
    </svg>
  );
}
<ProgressCircle aria-label="Loading…" value={60} />function ProgressCircle(props) {
  let {
    isIndeterminate,
    value,
    minValue = 0,
    maxValue = 100
  } = props;
  let { progressBarProps } = useProgressBar(props);
  let center = 16;
  let strokeWidth = 4;
  let r = 16 - strokeWidth;
  let c = 2 * r * Math.PI;
  let percentage = isIndeterminate
    ? 0.25
    : (value - minValue) / (maxValue - minValue);
  let offset = c - percentage * c;
  return (
    <svg
      {...progressBarProps}
      width={32}
      height={32}
      viewBox="0 0 32 32"
      fill="none"
      strokeWidth={strokeWidth}
    >
      <circle
        role="presentation"
        cx={center}
        cy={center}
        r={r}
        stroke="gray"
      />
      <circle
        role="presentation"
        cx={center}
        cy={center}
        r={r}
        stroke="orange"
        strokeDasharray={` `}
        strokeDashoffset={offset}
        transform="rotate(-90 16 16)"
      >
        {props.isIndeterminate &&
          (
            <animateTransform
              attributeName="transform"
              type="rotate"
              begin="0s"
              dur="1s"
              from="0 16 16"
              to="360 16 16"
              repeatCount="indefinite"
            />
          )}
      </circle>
    </svg>
  );
}
<ProgressCircle aria-label="Loading…" value={60} />function ProgressCircle(
  props
) {
  let {
    isIndeterminate,
    value,
    minValue = 0,
    maxValue = 100
  } = props;
  let {
    progressBarProps
  } = useProgressBar(
    props
  );
  let center = 16;
  let strokeWidth = 4;
  let r = 16 -
    strokeWidth;
  let c = 2 * r *
    Math.PI;
  let percentage =
    isIndeterminate
      ? 0.25
      : (value -
        minValue) /
        (maxValue -
          minValue);
  let offset = c -
    percentage * c;
  return (
    <svg
      {...progressBarProps}
      width={32}
      height={32}
      viewBox="0 0 32 32"
      fill="none"
      strokeWidth={strokeWidth}
    >
      <circle
        role="presentation"
        cx={center}
        cy={center}
        r={r}
        stroke="gray"
      />
      <circle
        role="presentation"
        cx={center}
        cy={center}
        r={r}
        stroke="orange"
        strokeDasharray={` `}
        strokeDashoffset={offset}
        transform="rotate(-90 16 16)"
      >
        {props
          .isIndeterminate &&
          (
            <animateTransform
              attributeName="transform"
              type="rotate"
              begin="0s"
              dur="1s"
              from="0 16 16"
              to="360 16 16"
              repeatCount="indefinite"
            />
          )}
      </circle>
    </svg>
  );
}
<ProgressCircle
  aria-label="Loading…"
  value={60}
/>Usage#
The following examples show how to use the ProgressBar and ProgressCircle components created in the above example.
Custom value scale#
By default, the value prop represents the current percentage of progress, as the minimum and maximum values default to 0 and 100, respectively. Alternatively, a different scale can be used by setting the minValue and maxValue props.
<ProgressBar
  label="Loading…"
  minValue={50}
  maxValue={150}
  value={100} /><ProgressBar
  label="Loading…"
  minValue={50}
  maxValue={150}
  value={100} /><ProgressBar
  label="Loading…"
  minValue={50}
  maxValue={150}
  value={100} />Value formatting#
Values are formatted as a percentage by default, but this can be modified by using the formatOptions prop to specify a different format.
formatOptions is compatible with the option parameter of Intl.NumberFormat and is applied based on the current locale.
<ProgressBar
  label="Sending…"
  formatOptions={{style: 'currency', currency: 'JPY'}}
  value={60} /><ProgressBar
  label="Sending…"
  formatOptions={{style: 'currency', currency: 'JPY'}}
  value={60} /><ProgressBar
  label="Sending…"
  formatOptions={{
    style: 'currency',
    currency: 'JPY'
  }}
  value={60}
/>Custom value label#
The valueLabel prop allows the formatted value to be replaced with a custom string.
<ProgressBar
  label="Feeding…"
  valueLabel="30 of 100 dogs"
  value={30} /><ProgressBar
  label="Feeding…"
  valueLabel="30 of 100 dogs"
  value={30} /><ProgressBar
  label="Feeding…"
  valueLabel="30 of 100 dogs"
  value={30}
/>Indeterminate#
The isIndeterminate prop can be used to represent an indeterminate operation. The ProgressCircle component created above
implements an animation to indicate this visually.
<ProgressCircle
  aria-label="Loading…"
  isIndeterminate /><ProgressCircle
  aria-label="Loading…"
  isIndeterminate /><ProgressCircle
  aria-label="Loading…"
  isIndeterminate />Internationalization#
Value formatting#
useProgressBar will handle localized formatting of the value label for accessibility
automatically. This is returned in the aria-valuetext prop in progressBarProps. You
can use this to create a visible label if needed and ensure that it is formatted correctly.
The number formatting can also be controlled using the formatOptions prop.
RTL#
In right-to-left languages, the progress bar should be mirrored. The label is right-aligned, the value is left-aligned, and the fill progresses from right to left. Ensure that your CSS accounts for this.