Slider
A value picked from a range by dragging a handle along a track.
import { Slider } from "@delacour/native-ui/slider";<Slider value={volume} onChange={setVolume}>
<Slider.Output />
<Slider.Track>
{() => (
<>
<Slider.Fill />
<Slider.Thumb />
</>
)}
</Slider.Track>
</Slider>Colours: default, primary, success, warning, danger, info. Sizes: sm, md,
lg — the groove's thickness, the thumb's diameter and the readout's type step.
Orientations: horizontal, vertical.
The package's first drag-driven control, and its first Gesture.Pan().
Anatomy

import { Slider } from "@delacour/native-ui/slider";import type { ReactElement } from "react";export function Demo(): ReactElement { return ( <Slider defaultValue={30}> <Slider.Output /> <Slider.Track> <Slider.Fill /> <Slider.Thumb testID="slider-thumb" /> </Slider.Track> </Slider> );}
import { SLIDER_SIZES, Slider } from "@delacour/native-ui/slider";import { Text } from "@delacour/native-ui/text";import type { ReactElement } from "react";import { View } from "react-native";export function Demo(): ReactElement { return ( <View className="gap-5"> {SLIDER_SIZES.map((size) => ( <View className="gap-2" key={size}> <Text.Caption color="muted">{size}</Text.Caption> <Slider defaultValue={50} size={size}> <Slider.Output /> <Slider.Track> <Slider.Fill /> <Slider.Thumb testID={`thumb-${size}`} /> </Slider.Track> </Slider> </View> ))} </View> );}| Part | What it is |
|---|---|
Slider | Owns the value, the gesture and the axes |
Slider.Output | The current value, formatted. Give it a function to label it yourself |
Slider.Track | The groove the thumbs run along, and the surface the drag is claimed on |
Slider.Fill | The painted part of the groove, from the minimum — or between two thumbs |
Slider.Thumb | The grab handle. index picks which value it drives in a range |
The anatomy is written out, never assembled from props. A range's thumb count is data, so
Slider.Track takes a function and is handed the settled state to map over. Radio composes its
own indicator in because a radio has exactly one; a slider does not know how many it has until it
is told.
Ranges

import { Slider } from "@delacour/native-ui/slider";import { Text } from "@delacour/native-ui/text";import { type ReactElement, useState } from "react";import { View } from "react-native";const CURRENCY = { currency: "NZD", style: "currency", maximumFractionDigits: 0 } as const;export function Demo(): ReactElement { const [price, setPrice] = useState<number[]>([200, 800]); return ( <Slider color="success" formatOptions={CURRENCY} maxValue={1000} onChange={(next) => setPrice(next as number[])} step={10} value={price} > <View className="flex-row items-center justify-between"> <Text.Label>Price range</Text.Label> <Slider.Output /> </View> <Slider.Track> {({ values }) => ( <> <Slider.Fill /> {values.map((_, index) => ( <Slider.Thumb index={index} key={index} testID={`thumb-${index}`} /> ))} </> )} </Slider.Track> </Slider> );}Pass an array and the slider becomes a range, one thumb per entry.
<Slider value={[20, 80]} onChange={setRange}>
<Slider.Track>
{({ values }) => (
<>
<Slider.Fill />
{values.map((_, index) => (
<Slider.Thumb index={index} key={index} />
))}
</>
)}
</Slider.Track>
</Slider>Which shape you speak is locked on first render. A slider that silently started reporting an array to a caller holding a number would be a bug with no error attached, so a flip warns once in development and the slider keeps following you.
The whole track is the target

import { SLIDER_COLORS, Slider } from "@delacour/native-ui/slider";import { Text } from "@delacour/native-ui/text";import type { ReactElement } from "react";import { View } from "react-native";export function Demo(): ReactElement { return ( <View className="gap-5"> {SLIDER_COLORS.map((color) => ( <View className="gap-2" key={color}> <Text.Caption color="muted">{color}</Text.Caption> <Slider color={color} defaultValue={65}> <Slider.Track> <Slider.Fill /> <Slider.Thumb testID={`thumb-${color}`} /> </Slider.Track> </Slider> </View> ))} </View> );}
import { Slider } from "@delacour/native-ui/slider";import type { ReactElement } from "react";import { View } from "react-native";const VERTICAL_COLORS = ["default", "success", "warning"] as const;export function Demo(): ReactElement { return ( <View className="h-56 flex-row justify-around"> {VERTICAL_COLORS.map((color) => ( <Slider color={color} defaultValue={45} key={color} orientation="vertical"> <Slider.Track> <Slider.Fill /> <Slider.Thumb testID={`thumb-${color}`} /> </Slider.Track> </Slider> ))} </View> );}Touching down grabs the nearest thumb and moves it to the finger; there is no separate tap mode.
Slider.Thumb holds no gesture of its own, which is what makes a press on empty track lift the
handle it is about to move.
That is also why the accessibility role and actions live on the thumb — with no gesture there, they are the only path assistive technology has to the value.
Two change callbacks
onChange fires on every change during the drag. onChangeEnd fires once, when the drag is
released — that is where a network write belongs.
Steps and haptics
step is the increment the value snaps to; 0 is continuous. Haptics are rate-limited by
distance rather than a clock (SLIDER_HAPTIC_MIN_TRAVEL), a continuous slider never ticks, and
both ends of the range always do.
Gotchas
A vertical slider needs a definite height
It counts up from the bottom and fills whatever it is given, so a wrapper with h-48 or flex-1
is not optional.
Inside a ScrollView on iOS
The slider feels late until the scrollable sets delaysContentTouches={false}. That is a prop at
your call site, not something the component can set.
API
Prop
Type




