Slider
Sliders let users select a value or range of values from a continuous or discrete set. Follows the Material Design 3 Slider specification.
The component supports four MD3 configurations from a single API:
- Continuous — single thumb, any value within range
- Discrete — single thumb that snaps to a
step, with auto-rendered tick marks - Range — two thumbs (
value={[low, high]}) - Centered — active track fills from the midpoint to the thumb (useful for symmetric values like balance)
Usage
import { Slider } 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 [value, setValue] = useState(0.4)
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 24 }}>
<Slider value={value} onValueChange={(v) => setValue(v as number)} />
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Discrete
Set step to a positive number to snap to fixed increments. Tick marks are rendered automatically.
import { Slider, Typography } 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 [value, setValue] = useState(40)
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 24, gap: 12 }}>
<Slider
value={value}
onValueChange={(v) => setValue(v as number)}
minimumValue={0}
maximumValue={100}
step={10}
/>
<Typography variant="bodySmall">Value: {value}</Typography>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Range
Pass a [low, high] tuple for a two-thumb range slider. Both thumbs respect step when set.
import { Slider, Typography } 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 [range, setRange] = useState([25, 75])
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 24, gap: 12 }}>
<Slider
value={range}
onValueChange={(v) => setRange(v)}
minimumValue={0}
maximumValue={100}
showValueLabel="always"
/>
<Typography variant="bodySmall">
{Math.round(range[0])} – {Math.round(range[1])}
</Typography>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Centered
Pass centered for a slider where the active track fills outward from the midpoint instead of from the start. Useful for symmetric values like balance, EQ, or zoom adjustments.
import { Slider, Typography } 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 [balance, setBalance] = useState(0)
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 24, gap: 12 }}>
<Slider
value={balance}
onValueChange={(v) => setBalance(v as number)}
minimumValue={-50}
maximumValue={50}
step={5}
centered
showValueLabel="always"
/>
<Typography variant="bodySmall">Balance: {balance}</Typography>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
With Icons
Decorate either side of the track with startIcon and endIcon — useful for volume, brightness, or other affordances where the bounds need a visual label.
import { Slider } 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 [volume, setVolume] = useState(0.6)
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 24 }}>
<Slider
value={volume}
onValueChange={(v) => setVolume(v as number)}
startIcon="volume-low"
endIcon="volume-high"
/>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Value label
The value-label bubble appears above the active thumb during a drag by default. Pass 'always' to keep it visible, or false to hide it. Use formatValueLabel to customize the displayed string.
<Slider value={value} onValueChange={setValue} showValueLabel="always" />
<Slider
value={price}
onValueChange={setPrice}
minimumValue={0}
maximumValue={1000}
step={10}
formatValueLabel={(v) => `$${v}`}
/>
Custom colors
containerColor overrides the active track, contentColor overrides the thumb, and inactiveTrackColor overrides the inactive track. Ticks and stop indicators on the active segment stay onPrimary per MD3 and are not derived from these overrides. The MD3 disabled treatment is preserved regardless of overrides.
import { Slider } 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 [value, setValue] = useState(0.5)
return (
<SafeAreaProvider>
<ThemeProvider>
<View style={{ flex: 1, justifyContent: 'center', padding: 24 }}>
<Slider
value={value}
onValueChange={(v) => setValue(v as number)}
containerColor="#2E7D32"
contentColor="#1B5E20"
inactiveTrackColor="#C8E6C9"
/>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Disabled
import { Slider } 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, justifyContent: 'center', padding: 24 }}>
<Slider value={0.7} disabled />
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Controlled vs uncontrolled
Pass value for a controlled slider (you own state, mutations come through onValueChange). Omit value and pass defaultValue for an uncontrolled slider that manages its own state internally.
// Controlled
<Slider value={value} onValueChange={setValue} />
// Uncontrolled — pass `[low, high]` to enable range mode
<Slider defaultValue={[20, 80]} onSlidingComplete={(v) => console.log(v)} />
onValueChange fires continuously during a drag; onSlidingComplete fires once when the user releases the thumb — useful for committing expensive updates (network calls, large recomputes) only on release.
Accessibility
The track has accessibilityRole="adjustable" and exposes accessibilityValue with min/max/now (rounded to integers; native a11y APIs require integer values) and a formatted text field that screen readers read aloud. Pass accessibilityLabel to describe what the slider controls.
<Slider
value={volume}
onValueChange={setVolume}
accessibilityLabel="Output volume"
/>
Props
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
value | SliderValue | - | No | Current value. Pass a number for single-thumb mode or `[low, high]` for range mode. Use `defaultValue` for uncontrolled usage. |
defaultValue | SliderValue | - | No | Initial value for uncontrolled usage. Pass a `[low, high]` tuple to enable range mode. |
onValueChange | (value: SliderValue) => void | - | No | Called continuously during a drag with the latest value. |
onSlidingComplete | (value: SliderValue) => void | - | No | Called once when the user releases the thumb. |
minimumValue | number | 0 | No | Lowest allowed value. |
maximumValue | number | 1 | No | Highest allowed value. |
step | number | 0 | No | Step interval. Set to a positive number to enable discrete mode (values snap to multiples of `step` and tick marks are shown by default). Omit or set to `0` for a continuous slider. |
centered | boolean | | No | Centered slider — the active track fills from the midpoint to the thumb instead of from the start. Useful for symmetric values like balance or EQ adjustments. Single-thumb mode only; ignored for range sliders. |
showTickMarks | boolean | - | No | Whether to render tick marks along the track. Defaults to `true` when `step > 0`, otherwise `false`. Pass an explicit boolean to override. |
showValueLabel | boolean | "always" | | No | Show the value bubble above the thumb. `true` shows it during drag only, `'always'` keeps it visible, `false` hides it entirely. |
formatValueLabel | (value: number) => string | - | No | Custom formatter for the value-label text. Defaults to integer rounding for discrete sliders and 2-decimal rounding for continuous sliders. |
startIcon | IconSource | - | No | Optional icon decoration shown at the start (leading edge) of the track. Accepts a string name (resolved via the theme's `iconResolver`), a pre-rendered element, or a render function. |
endIcon | IconSource | - | No | Optional icon decoration shown at the end (trailing edge) of the track. |
containerColor | string | - | No | Override the active track color. Hover/press state-layer colors on the thumb are derived from `contentColor` (or the active track color when unset). |
contentColor | string | - | No | Override the thumb color. |
inactiveTrackColor | string | - | No | Override the inactive track color. |
disabled | boolean | | No | Disable interaction. Renders the standard MD3 disabled treatment (38% `onSurface`); `containerColor`/`contentColor` are ignored. |
style | StyleProp<ViewStyle> | - | No | Style applied to the root container. |
onKeyDown | (event: { nativeEvent: { key?: string; }; }) => void | - | No | - |