Switch
A switch is a simple, two-option control that can be toggled between two states — on and off. It's commonly used in settings or preferences menus where users can enable or disable features.
import {Switch} from "@qualcomm-ui/react/switch"Examples
Simple
Basic switch using the simple API with a label.
<Switch label="Switch" />Composite
Build with the composite API for granular control. This API requires you to provide each subcomponent, but gives you full control over the structure and layout.
<Switch.Root>
<Switch.HiddenInput />
<Switch.Label>Label</Switch.Label>
<Switch.Control />
</Switch.Root>States
Based on the inputs, the switch will render as checked or unchecked.
<Switch />
<Switch defaultChecked />Disabled State
When disabled is true, the switch becomes non-interactive and is rendered with reduced opacity to indicate its unavailable state.
<Switch disabled label="Disabled" />Sizes
Use the size prop to change the size of the switch.
<Switch label="Small (sm)" size="sm" />
<Switch label="Medium (md)" size="md" /> {/* default */}Controlled State
Set the initial value using the defaultChecked prop, or use checked and onCheckedChange to control the value manually. These props follow our controlled state pattern.
import {type ReactElement, useState} from "react"
import {Switch} from "@qualcomm-ui/react/switch"
export default function SwitchControlledDemo(): ReactElement {
const [checked, setChecked] = useState<boolean>(false)
return (
<Switch.Root
checked={checked}
onCheckedChange={(nextState) => {
console.log("Switch state change:", nextState)
setChecked(nextState)
}}
>
<Switch.HiddenInput />
<Switch.Control />
<Switch.Label>Label</Switch.Label>
</Switch.Root>
)
}
Forms
Choose the form library that fits your needs—we've built examples with React Hook Form and Tanstack Form to get you started.
React Hook Form
Use React Hook Form to handle the switch state and validation. ArkType works great for schema validation if you need it.
import type {ReactElement} from "react"
import {arktypeResolver} from "@hookform/resolvers/arktype"
import {type} from "arktype"
import {Controller, useForm} from "react-hook-form"
import {Button} from "@qualcomm-ui/react/button"
import {Switch} from "@qualcomm-ui/react/switch"
interface FormData {
acceptTerms: boolean
newsletter: boolean
}
const acceptTermsSchema = type("boolean")
.narrow((value: boolean) => value === true)
.configure({
message: "Please accept the Terms of Service to continue",
})
const FormSchema = type({
// must be true
acceptTerms: acceptTermsSchema,
// can be true or false
newsletter: "boolean",
})
export default function SwitchReactHookFormDemo(): ReactElement {
const {control, handleSubmit} = useForm<FormData>({
defaultValues: {
acceptTerms: false,
newsletter: true,
},
resolver: arktypeResolver(FormSchema),
})
return (
<form
className="flex w-56 flex-col gap-2"
onSubmit={(e) => {
void handleSubmit((data) => console.log(data))(e)
}}
>
<Controller
control={control}
name="newsletter"
render={({field: {name, onChange, value, ...fieldProps}}) => {
return (
<Switch.Root
checked={value}
name={name}
onCheckedChange={onChange}
{...fieldProps}
>
<Switch.HiddenInput />
<Switch.Control />
<Switch.Label>Subscribe to our Newsletter</Switch.Label>
</Switch.Root>
)
}}
/>
<Controller
control={control}
name="acceptTerms"
render={({
field: {onChange, value, ...fieldProps},
fieldState: {error},
}) => {
return (
<Switch.Root
checked={value}
invalid={!!error?.message}
onCheckedChange={onChange}
{...fieldProps}
>
<Switch.HiddenInput />
<Switch.Control />
<Switch.Label>Accept Terms of Service</Switch.Label>
<Switch.ErrorText>{error?.message}</Switch.ErrorText>
</Switch.Root>
)
}}
/>
<Button
className="mt-1"
emphasis="primary"
size="sm"
type="submit"
variant="fill"
>
Submit
</Button>
</form>
)
}
Tanstack Form
Tanstack Form handles switch validation with its built-in validators.
import type {ReactElement} from "react"
import {useForm} from "@tanstack/react-form"
import {Button} from "@qualcomm-ui/react/button"
import {Switch} from "@qualcomm-ui/react/switch"
interface FormData {
acceptTerms: boolean
newsletter: boolean
}
const defaultFormData: FormData = {
acceptTerms: false,
newsletter: true,
}
const errorMessage = "Please accept the Terms of Service to continue"
export default function SwitchTanstackFormDemo(): ReactElement {
const form = useForm({
defaultValues: defaultFormData,
onSubmit: async ({value}) => {
// Do something with form data
console.log(value)
},
})
return (
<form
className="flex w-56 flex-col gap-2"
onSubmit={(event) => {
event.preventDefault()
event.stopPropagation()
void form.handleSubmit()
}}
>
<form.Field name="newsletter">
{({handleChange, name, state}) => {
return (
<Switch.Root
checked={state.value}
name={name}
onCheckedChange={handleChange}
>
<Switch.HiddenInput />
<Switch.Control />
<Switch.Label>Subscribe to our Newsletter</Switch.Label>
</Switch.Root>
)
}}
</form.Field>
<form.Field
name="acceptTerms"
validators={{
onChange: (field) => (field.value ? undefined : errorMessage),
onSubmit: (field) => (field.value ? undefined : errorMessage),
}}
>
{({handleChange, name, state}) => {
const fieldError =
state.meta.errorMap["onChange"] || state.meta.errorMap["onSubmit"]
return (
<Switch.Root
checked={state.value}
invalid={!!fieldError}
name={name}
onCheckedChange={handleChange}
>
<Switch.HiddenInput />
<Switch.Control />
<Switch.Label>Accept Terms</Switch.Label>
<Switch.ErrorText>{fieldError}</Switch.ErrorText>
</Switch.Root>
)
}}
</form.Field>
<Button
className="mt-1"
emphasis="primary"
size="sm"
type="submit"
variant="fill"
>
Submit
</Button>
</form>
)
}
Tanstack form also supports ArkType.
Composite Guidelines
The composite elements are only intended to be used as direct descendants of the <Switch.Root> component.
/* Won't work alone ❌ */
<Switch.HiddenInput />
<Switch.Control />
/* Works as expected ✅ */
<Switch.Root>
<Switch.HiddenInput />
<Switch.Control />
</Switch.Root>Shortcuts
Control and Thumb
The switch control automatically renders the thumb if no child elements are supplied. This:
<Switch.Control />is equivalent to this:
<Switch.Control>
<Switch.Thumb />
</Switch.Control>API
<Switch.Root>
| Prop | Type | Default |
|---|---|---|
The controlled checked state of the switch | boolean | |
The initial checked state of the switch when rendered.
Use when you don't need to control the checked state of the switch. | boolean | |
The document's text/writing direction. | 'ltr' | 'rtl' | 'ltr' |
Whether the input is disabled. When true, prevents user interaction and
applies visual styling to indicate the disabled state. | boolean | |
The id of the form that the switch belongs to. | string | |
A root node to correctly resolve document in custom environments. i.e.,
Iframes, Electron. | () => | |
The ids of the elements that are associated with the switch. These will be
automatically generated if omitted. | Partial<{ | |
Controls the visual error state of the input. When true, applies semantic error
styling to indicate validation failure. | boolean | |
The name of the input field in a switch.
Useful for form submission. | string | |
The callback invoked when the checked state changes. | ( | |
The callback invoked when the field is focused. | ( | |
Whether the input is read-only. When true, prevents user interaction while
keeping the input focusable and visible. | boolean | |
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement | |
Whether the input is required. When true, the input must have a value for form
validation to pass. | boolean | |
Size of the component and its label. | 'sm' | 'md' | "md" |
The value of switch input. Useful for form submission. | string | "on" |
booleanboolean'ltr' | 'rtl'
booleanstring() =>
| Node
| ShadowRoot
| Document
Partial<{
errorText: string
hiddenInput: string
label: string
root: string
}>
booleanstring(
checked: boolean,
) => void
(
focused: boolean,
) => void
boolean| ReactElement
| ((
props: object,
) => ReactElement)
boolean'sm' | 'md'
string<Switch>
The Switch component supports all props from Switch.Root, plus the additional props listed below.
| Prop | Type |
|---|---|
Props applied to the control element. | { |
string | |
Props applied to the error text element. | { |
Props applied to the hidden input element. | any |
Optional label describing the element. Recommended. This element is
automatically associated with the component's input element for
accessibility. | |
Props applied to the label element. | { |
Props applied to the thumb element. | { |
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
{
children?: ReactNode
icon?:
| LucideIcon
| ReactNode
id?: string
render?:
| Element
| ((
props: Props,
) => Element)
}
any{
children?: ReactNode
id?: string
render?:
| Element
| ((
props: Props,
) => Element)
}
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
<label>element by default.