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 '@onlynative/components'
import { ThemeProvider } from '@onlynative/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 '@onlynative/components'
import { ThemeProvider } from '@onlynative/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 '@onlynative/components'
import { ThemeProvider } from '@onlynative/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 '@onlynative/components'
import { ThemeProvider } from '@onlynative/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 '@onlynative/components'
import { ThemeProvider } from '@onlynative/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 the active-track tick contrast), and inactiveTrackColor overrides the inactive track. The MD3 disabled treatment is preserved regardless of overrides.
import { Slider } from '@onlynative/components'
import { ThemeProvider } from '@onlynative/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 '@onlynative/components'
import { ThemeProvider } from '@onlynative/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 (and the tick-mark color over the active track). |
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. |