Slider

Free · MITReact Native · Expo

Single and range sliders whose thumbs follow the finger 1:1 and settle onto the nearest step on release, with a value bubble that grows out of the thumb while dragging and optional step ticks that tick.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add https://goodui.dev/native/r/slider.json

Then run npx expo install --fix so react-native-gesture-handler, react-native-reanimated match your Expo SDK. Files land in components/goodui-native/slider.

  • Wrap the app in GestureHandlerRootView and ThemeProvider.

Usage

import { useState } from "react";
import { RangeSlider, Slider } from "@/components/goodui-native/slider/slider";

export function Filters() {
  const [distance, setDistance] = useState(10);
  const [price, setPrice] = useState<[number, number]>([40, 180]);
  return (
    <>
      <Slider accessibilityLabel="Distance" value={distance} onValueChange={setDistance} min={1} max={50} formatValue={(v) => `${v} km`} />
      <RangeSlider
        accessibilityLabel="Price"
        value={price}
        onValueChange={setPrice}
        onSlidingComplete={(range) => refetch(range)}
        min={0}
        max={300}
        step={10}
        formatValue={(v) => `$${v}`}
      />
    </>
  );
}

When to use

Use it for
  • Picking a value where the position matters more than the exact digit: volume, brightness, budget, distance.
  • A range: price filter, age range, time window (RangeSlider).
  • A small set of evenly spaced values with feedback: rating 1 to 5, tip percentage (step with showTicks).
Reach for something else
  • An exact number the person types: use text-field.
  • Small counts changed by one (guests, quantity): use quantity-stepper.
  • Two to five named options: use segmented-control.

Examples

Stepped with ticks and an always-visible value

<Slider accessibilityLabel="Tip" defaultValue={15} min={0} max={30} step={5} showTicks showValue="always" formatValue={(v) => `${v}%`} />

Continuous, small, status tone

<Slider size="sm" tone="warning" step={0} showValue="never" accessibilityLabel="Brightness" />

API reference

Slider

PropTypeDefault
min
Lowest value. Default 0.
number—
max
Highest value. Default 100.
number—
step
Values snap to multiples of this. Default 1. `0` is continuous.
number—
showTicks
Dots at every step, and a select haptic each time the value crosses one. Shown only when there are 40 steps or fewer.
boolean—
showValue
Value bubble above the thumb: `drag` appears while dragging, `always` stays and grows while dragging, `never`.
"drag" | "always" | "never"—
formatValue
Text for a value in the bubble and for screen readers, for example `(v) => \`$${v}\``.
(value: number) => string—
tone
Fill color. `accent` by default; status tones only when the value means a status.
"success" | "warning" | "danger" | "accent"—
size
Thumb 22 (`sm`) or 28 pt (`md`). The touch area stays at least 44 pt tall.
"sm" | "md"—
disabled
Dims the control and ignores presses.
boolean—
haptic
`false` silences the tick and edge haptics.
boolean—
onSlidingStart
Called when a drag or tap starts changing the value.
() => void—
accessibilityLabel
Screen reader label, for example "Volume".
string—
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<SliderLabels>—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—
value
The value (controlled). Pair it with `onValueChange`.
number—
defaultValue
Initial value when uncontrolled. Defaults to `min`.
number—
onValueChange
Called while dragging, each time the snapped value changes.
(value: number) => void—
onSlidingComplete
Called once when the finger lifts, with the final value. Save or fetch here.
(value: number) => void—

RangeSlider

PropTypeDefault
min
Lowest value. Default 0.
number—
max
Highest value. Default 100.
number—
step
Values snap to multiples of this. Default 1. `0` is continuous.
number—
showTicks
Dots at every step, and a select haptic each time the value crosses one. Shown only when there are 40 steps or fewer.
boolean—
showValue
Value bubble above the thumb: `drag` appears while dragging, `always` stays and grows while dragging, `never`.
"drag" | "always" | "never"—
formatValue
Text for a value in the bubble and for screen readers, for example `(v) => \`$${v}\``.
(value: number) => string—
tone
Fill color. `accent` by default; status tones only when the value means a status.
"success" | "warning" | "danger" | "accent"—
size
Thumb 22 (`sm`) or 28 pt (`md`). The touch area stays at least 44 pt tall.
"sm" | "md"—
disabled
Dims the control and ignores presses.
boolean—
haptic
`false` silences the tick and edge haptics.
boolean—
onSlidingStart
Called when a drag or tap starts changing the value.
() => void—
accessibilityLabel
Screen reader label, for example "Volume".
string—
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<SliderLabels>—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—
value
The low and high values (controlled).
[number, number]—
defaultValue
Initial values when uncontrolled. Defaults to `[min, max]`.
[number, number]—
onValueChange
Called while dragging, each time either snapped value changes.
(value: [number, number]) => void—
onSlidingComplete
Called once when the finger lifts, with the final pair.
(value: [number, number]) => void—
minDistance
Smallest gap between the two values. Default one step.
number—
thumbLabels
Screen reader labels of the two thumbs. Same as `labels.minimum` and `labels.maximum`.
[string, string]—

Motion and haptics

  • Dragging: the thumb tracks the finger 1:1 (grabbing it keeps the finger's offset, grabbing the track brings the nearest thumb to the finger) and scales to 1.08.
  • Release: the thumb settles onto the step the value already shows on the snappy spring.
  • Tapping the track moves the nearest thumb there on the smooth spring.
  • The value bubble grows out of the top of the thumb while dragging (showValue 'drag' appears from nothing, 'always' grows from 84 %).
  • Outside value changes move the thumbs on the smooth spring.
  • Reduced motion: no thumb or bubble scaling (the bubble simply appears), release and outside changes jump to the value; dragging still follows the finger.
  • Haptic: select each time the value crosses a tick (with showTicks)
  • Haptic: select when the value reaches min or max
  • The gesture and all thumb, fill and bubble motion run on the UI thread.
  • React updates only when the snapped value changes, not per frame. Use onSlidingComplete for expensive work.

Accessibility

  • Each thumb is an adjustable element (a slider on the web) with min, max, now and the formatted text as its value.
  • Screen readers use the increment and decrement actions; one action moves one step (or 5 % of the range when continuous).
  • Range thumbs are labelled '<label>, Minimum' and '<label>, Maximum' (thumbLabels to localize).
  • On the web each thumb is focusable; arrow keys step it and show the value bubble while focused.
  • The touch area is 44 pt tall regardless of the visual size.

Notes for AI

  • Use Slider for one value and RangeSlider for a [low, high] pair. Both work controlled or uncontrolled.
  • onValueChange fires during the drag; put requests and saves in onSlidingComplete.
  • step={0} is continuous. showTicks only draws when there are 40 steps or fewer.
  • Always pass accessibilityLabel and a formatValue that adds the unit.
  • Leave room above the slider when showValue is 'drag': the bubble floats over the content above. 'always' reserves its own space.
  • Requires GestureHandlerRootView at the app root.