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/progress |
---|---|
version | 3.1.8 |
usage | import {useProgressBar} from '@react-aria/progress' |
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 | HTMLAttributes<HTMLElement> | Props for the progress bar container element. |
labelProps | HTMLAttributes<HTMLElement> | 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/progress';
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: 'gray'}}>
<div style={{width: barWidth, height: 10, background: 'orange'}} />
</div>
</div>
);
}
<ProgressBar label="Loading..." value={30} />
import {useProgressBar} from '@react-aria/progress';
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: 'gray' }}>
<div
style={{
width: barWidth,
height: 10,
background: 'orange'
}}
/>
</div>
</div>
);
}
<ProgressBar label="Loading..." value={30} />
import {useProgressBar} from '@react-aria/progress';
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:
'gray'
}}
>
<div
style={{
width:
barWidth,
height: 10,
background:
'orange'
}}
/>
</div>
</div>
);
}
<ProgressBar
label="Loading..."
value={30}
/>
Indeterminate#
Progress bars can represent an indeterminate operation. They may also be used to represent progress visually as a circle rather than as a line. The following example shows an indeterminate progress bar visualized as a circular spinner using SVG.
function Spinner() {
let {progressBarProps} = useProgressBar({
isIndeterminate: true,
'aria-label': 'Loading...'
});
let center = 16;
let strokeWidth = 4;
let r = 16 - strokeWidth;
let c = 2 * r * Math.PI;
let offset = c - (1 / 4) * 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={c}
strokeDashoffset={offset}>
<animateTransform
attributeName="transform"
type="rotate"
begin="0s"
dur="1s"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite" />
</circle>
</svg>
);
}
function Spinner() {
let {progressBarProps} = useProgressBar({
isIndeterminate: true,
'aria-label': 'Loading...'
});
let center = 16;
let strokeWidth = 4;
let r = 16 - strokeWidth;
let c = 2 * r * Math.PI;
let offset = c - (1 / 4) * 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={c}
strokeDashoffset={offset}>
<animateTransform
attributeName="transform"
type="rotate"
begin="0s"
dur="1s"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite" />
</circle>
</svg>
);
}
function Spinner() {
let {
progressBarProps
} = useProgressBar({
isIndeterminate:
true,
'aria-label':
'Loading...'
});
let center = 16;
let strokeWidth = 4;
let r = 16 -
strokeWidth;
let c = 2 * r *
Math.PI;
let offset = c -
(1 / 4) * 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={c}
strokeDashoffset={offset}
>
<animateTransform
attributeName="transform"
type="rotate"
begin="0s"
dur="1s"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite"
/>
</circle>
</svg>
);
}
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.