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.29.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 interdeterminate 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.