Skip to main content

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

PropTypeDefaultRequiredDescription
valueSliderValue-NoCurrent value. Pass a number for single-thumb mode or `[low, high]` for range mode. Use `defaultValue` for uncontrolled usage.
defaultValueSliderValue-NoInitial value for uncontrolled usage. Pass a `[low, high]` tuple to enable range mode.
onValueChange(value: SliderValue) => void-NoCalled continuously during a drag with the latest value.
onSlidingComplete(value: SliderValue) => void-NoCalled once when the user releases the thumb.
minimumValuenumber0NoLowest allowed value.
maximumValuenumber1NoHighest allowed value.
stepnumber0NoStep 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.
centeredbooleanNoCentered 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.
showTickMarksboolean-NoWhether to render tick marks along the track. Defaults to `true` when `step > 0`, otherwise `false`. Pass an explicit boolean to override.
showValueLabelboolean | "always"NoShow the value bubble above the thumb. `true` shows it during drag only, `'always'` keeps it visible, `false` hides it entirely.
formatValueLabel(value: number) => string-NoCustom formatter for the value-label text. Defaults to integer rounding for discrete sliders and 2-decimal rounding for continuous sliders.
startIconIconSource-NoOptional 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.
endIconIconSource-NoOptional icon decoration shown at the end (trailing edge) of the track.
containerColorstring-NoOverride the active track color. Hover/press state-layer colors on the thumb are derived from `contentColor` (or the active track color when unset).
contentColorstring-NoOverride the thumb color (and the tick-mark color over the active track).
inactiveTrackColorstring-NoOverride the inactive track color.
disabledbooleanNoDisable interaction. Renders the standard MD3 disabled treatment (38% `onSurface`); `containerColor`/`contentColor` are ignored.
styleStyleProp<ViewStyle>-NoStyle applied to the root container.