Skip to main content

Switch

Switches toggle the state of a single item on or off. Follows the Material Design 3 Switch specification.

Usage

import { Switch } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { useState } from 'react'
import { View } from 'react-native'

export default function App() {
const [isOn, setIsOn] = useState(false)
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Switch value={isOn} onValueChange={setIsOn} />
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Controlled or uncontrolled

Pass value with onValueChange to own the state, as above. Omit value and Switch manages its own, starting from defaultValue:

<Switch defaultValue onValueChange={handleChange} />

onValueChange still fires either way. In earlier prereleases, passing onValueChange without value did nothing visually — the callback fired but the control never moved. See the changelog if you are upgrading.

With Icons

Icons are opt-in — the MD3 default switch renders none. Pass selectedIcon (and optionally unselectedIcon) for the spec's "switch with icon" variant.

import { Switch } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { useState } from 'react'
import { View } from 'react-native'

export default function App() {
const [a, setA] = useState(true)
const [b, setB] = useState(true)
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16 }}>
<Switch value={a} onValueChange={setA} selectedIcon="check" />
<Switch
value={b}
onValueChange={setB}
selectedIcon="bell"
unselectedIcon="bell-off"
/>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

When unselectedIcon is provided, the thumb renders at the larger selected size in both states.

Disabled

import { Switch } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12 }}>
<Switch value={false} onValueChange={() => {}} disabled />
<Switch value onValueChange={() => {}} disabled />
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

Props

PropTypeDefaultRequiredDescription
valueboolean-NoWhether the switch is toggled on (controlled). Pass this together with `onValueChange` to own the state. Omit it and the component manages its own state, starting from `defaultValue`.
defaultValuebooleanNoInitial value when uncontrolled. Ignored once `value` is passed.
onValueChange(value: boolean) => void-NoCallback fired when the switch is toggled. Receives the new value.
selectedIconIconSource-NoIcon shown on the thumb when selected. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. Opt-in: the MD3 default switch renders no icons — pass `'check'` for the spec's "switch with icon" variant.
unselectedIconIconSource-NoIcon shown on the thumb when unselected. When provided, the thumb renders at the larger selected size. Same accepted forms as `selectedIcon`.
containerColorstring-NoOverride the track color. State-layer colors (hover, press) are derived automatically.
contentColorstring-NoOverride the thumb and icon color.
styleStyleProp<ViewStyle>-NoStyle applied to the root container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling.
onKeyDown(event: { nativeEvent: { key?: string; }; }) => void-No-