useSwitch
Provides the behavior and accessibility implementation for a switch component. A switch is similar to a checkbox, but represents on/off values as opposed to selection.
install | yarn add @react-aria/switch |
---|---|
version | 3.1.3 |
usage | import {useSwitch} from '@react-aria/switch' |
API#
useSwitch(
props: AriaSwitchProps,
state: ToggleState,
ref: RefObject<HTMLInputElement>
): SwitchAria
Features#
There is no native HTML element with switch styling. <input type="checkbox">
is the closest semantically, but isn't styled or exposed to assistive technology
as a switch. useSwitch
helps achieve accessible switches that can be styled as needed.
- Built with a native HTML
<input>
element, which can be optionally visually hidden to allow custom styling - Full support for browser features like form autofill
- Keyboard focus management and cross browser normalization
- Labeling support for screen readers
- Exposed as a switch to assistive technology via ARIA
Anatomy#
A switch consists of a visual selection indicator and a label. Users may click or touch a switch to toggle the selection state, or use the Tab key to navigate to it and the Space key to toggle it.
useSwitch
returns props to be spread onto its input element:
Name | Type | Description |
inputProps | InputHTMLAttributes<HTMLInputElement> | Props for the input element. |
Selection state is managed by the useToggleState
hook in @react-stately/toggle
. The state object should be passed as an option to useSwitch
.
In most cases, switches should have a visual label. If the switch does not have a visible label,
an aria-label
or aria-labelledby
prop must be passed instead to identify the element to assistive
technology.
Example#
This example uses SVG to build the switch, with a visually hidden native input to represent
the switch for accessibility. This is possible using
the <VisuallyHidden
>
utility component from @react-aria/visually-hidden
. It is still in the DOM and accessible to assistive technology,
but invisible. The SVG element is the visual representation, and is hidden from screen readers
with aria-hidden
.
For keyboard accessibility, a focus ring is important to indicate which element has keyboard focus.
This is implemented with the useFocusRing
hook from @react-aria/focus
. When isFocusVisible
is true, an extra SVG element is rendered to
indicate focus. The focus ring is only visible when the user is interacting with a keyboard,
not with a mouse or touch.
import {VisuallyHidden} from '@react-aria/visually-hidden';
import {useToggleState} from '@react-stately/toggle';
import {useFocusRing} from '@react-aria/focus';
function Switch(props) {
let state = useToggleState(props);
let ref = ReactuseRef();
let {inputProps} = useSwitch(props state ref);
let {isFocusVisible focusProps} = useFocusRing();
return (
<label style={display: 'flex' alignItems: 'center'}>
<VisuallyHidden>
<input ...inputProps ...focusProps ref= ref />
</VisuallyHidden>
<svg width=40 height=24 aria-hidden="true" style={marginRight: 4}>
<rect
x=4
y=4
width=32
height=16
rx=8
fill= stateisSelected ? 'orange' : 'gray'
/>
<circle cx= stateisSelected ? 28 : 12 cy=12 r=5 fill="white" />
isFocusVisible && (
<rect
x=1
y=1
width=38
height=22
rx=11
fill="none"
stroke="orange"
strokeWidth=2
/>
)
</svg>
propschildren
</label>
);
}
<Switch>Test</Switch>
import {VisuallyHidden} from '@react-aria/visually-hidden';
import {useToggleState} from '@react-stately/toggle';
import {useFocusRing} from '@react-aria/focus';
function Switch(props) {
let state = useToggleState(props);
let ref = ReactuseRef();
let {inputProps} = useSwitch(props state ref);
let {isFocusVisible focusProps} = useFocusRing();
return (
<label style={display: 'flex' alignItems: 'center'}>
<VisuallyHidden>
<input ...inputProps ...focusProps ref= ref />
</VisuallyHidden>
<svg
width=40
height=24
aria-hidden="true"
style={marginRight: 4}>
<rect
x=4
y=4
width=32
height=16
rx=8
fill= stateisSelected ? 'orange' : 'gray'
/>
<circle
cx= stateisSelected ? 28 : 12
cy=12
r=5
fill="white"
/>
isFocusVisible && (
<rect
x=1
y=1
width=38
height=22
rx=11
fill="none"
stroke="orange"
strokeWidth=2
/>
)
</svg>
propschildren
</label>
);
}
<Switch>Test</Switch>
import {VisuallyHidden} from '@react-aria/visually-hidden';
import {useToggleState} from '@react-stately/toggle';
import {useFocusRing} from '@react-aria/focus';
function Switch(props) {
let state = useToggleState(
props
);
let ref = ReactuseRef();
let {
inputProps
} = useSwitch(
props
state
ref
);
let {
isFocusVisible
focusProps
} = useFocusRing();
return (
<label
style={
display: 'flex'
alignItems:
'center'
}>
<VisuallyHidden>
<input
...inputProps
...focusProps
ref= ref
/>
</VisuallyHidden>
<svg
width=40
height=24
aria-hidden="true"
style={
marginRight: 4
}>
<rect
x=4
y=4
width=32
height=16
rx=8
fill=
stateisSelected
? 'orange'
: 'gray'
/>
<circle
cx=
stateisSelected
? 28
: 12
cy=12
r=5
fill="white"
/>
isFocusVisible && (
<rect
x=1
y=1
width=38
height=22
rx=11
fill="none"
stroke="orange"
strokeWidth=
2
/>
)
</svg>
propschildren
</label>
);
}
<Switch>Test</Switch>
Internationalization#
RTL#
In right-to-left languages, switches should be mirrored. The switch should be placed on the right side of the label. Ensure that your CSS accounts for this.