packages/react-aria/docs/switch/useSwitch.mdx
{/* Copyright 2020 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}
import {Layout} from '@react-spectrum/docs'; export default Layout;
import docs from 'docs:@react-aria/switch'; import hiddenDocs from 'docs:@react-aria/visually-hidden'; import focusDocs from 'docs:@react-aria/focus'; import statelyDocs from 'docs:@react-stately/toggle'; import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; import {Keyboard} from '@react-spectrum/text'; import packageData from '@react-aria/switch/package.json'; import Anatomy from './anatomy.svg';
<PageDescription>{docs.exports.useSwitch.description}</PageDescription>
<HeaderInfo packageData={packageData} componentNames={['useSwitch']} sourceData={[ {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/switch/'} ]} />
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.
<input> element, which can be optionally visually
hidden to allow custom stylingA 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 <Keyboard>Tab</Keyboard> key to navigate to it and the <Keyboard>Space</Keyboard> key to toggle it.
useSwitch returns props to be spread onto its input element:
<TypeContext.Provider value={docs.links}> <InterfaceType properties={docs.links[docs.exports.useSwitch.return.id].properties} /> </TypeContext.Provider>
Selection state is managed by the <TypeLink links={statelyDocs.links} type={statelyDocs.exports.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.
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 <<TypeLink links={hiddenDocs.links} type={hiddenDocs.exports.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 <TypeLink links={focusDocs.links} type={focusDocs.exports.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 {useSwitch} from '@react-aria/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 = React.useRef(null);
let {inputProps} = useSwitch(props, state, ref);
let {isFocusVisible, focusProps} = useFocusRing();
return (
<label style={{display: 'flex', alignItems: 'center', opacity: props.isDisabled ? 0.4 : 1}}>
<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={state.isSelected ? 'orange' : 'gray'} />
<circle
cx={state.isSelected ? 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>
{props.children}
</label>
);
}
<Switch>Low power mode</Switch>
The following examples show how to use the Switch component created in the above example.
Switches are not selected by default. The defaultSelected prop can be used to set the default state.
<Switch defaultSelected>Wi-Fi</Switch>
The isSelected prop can be used to make the selected state controlled. The onChange event is fired when the user presses the switch, and receives the new value.
function Example() {
let [selected, setSelected] = React.useState(false);
return (
<>
<Switch onChange={setSelected}>Low power mode</Switch>
<p>{selected ? 'Low' : 'High'} power mode active.</p>
</>
);
}
Switches can be disabled using the isDisabled prop.
<Switch isDisabled>Airplane Mode</Switch>
The isReadOnly prop makes the selection immutable. Unlike isDisabled, the Switch remains focusable.
See the MDN docs for more information.
<Switch isSelected isReadOnly>Bluetooth</Switch>
Switch supports the name and value props for integration with HTML forms.
<Switch name="power" value="low">Low power mode</Switch>
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.