Skip to content

Slider

@symbiote-native/slider wraps the RNCSlider native view from @react-native-community/slider so every SymbioteNative adapter can render it — without importing that library’s React component body. It is the reference implementation of the third-party native-view wrapper pattern: the recipe to follow for any future community RN view. Its counterpart is splash screen, which wraps a community library that ships an imperative native module and no view at all — both are autolinked through a react-native.config.cjs + podspec proxy, unlike the expo-modules-core packages such as local auth, whose native code expo-modules-autolinking discovers instead.

OS platform Support
iOS ✅ live
Android ✅ live
Framework adapter Support
React ✅ live
Vue ✅ live
Angular ✅ live
Svelte ✅ live
Terminal window
pnpm add @react-native-community/slider

@symbiote-native/slider itself is a workspace package (packages/slider), not yet published — add it as a workspace dependency the same way the examples do.

import { useState } from 'react';
import { Slider } from '@symbiote-native/slider/react';
export default function VolumeControl() {
const [volume, setVolume] = useState(0.5);
return (
<Slider
value={volume}
minimumValue={0}
maximumValue={1}
step={0.05}
onValueChange={setVolume}
minimumTrackTintColor="#61dafb"
thumbTintColor="#61dafb"
/>
);
}

renderStepNumber draws the library’s built-in step dots. To draw your own marker per step, pass a StepMarker (React), fill the #stepMarker scoped slot (Vue), or pass a stepMarker snippet (Svelte) — all three receive { stepMarked, currentValue, index, min, max }:

<Slider
value={2}
minimumValue={0}
maximumValue={4}
step={1}
StepMarker={({ stepMarked }) => (
<View style={{ width: 8, height: 8, borderRadius: 4, opacity: stepMarked ? 1 : 0.4 }} />
)}
/>
<Slider value={2} minimumValue={0} maximumValue={4} step={1}>{#snippet stepMarker({ stepMarked })}<View style={{ width: 8, height: 8, borderRadius: 4, opacity: stepMarked ? 1 : 0.4 }} />{/snippet}</Slider>

Every prop below is framework-agnostic and shared verbatim by every adapter, except StepMarker, which is per-adapter because it returns a framework element (a React component, a Vue scoped slot, or a Svelte snippet) — see shared vs framework-specific.

Prop Type Default Description
value number 0 The slider’s position. Uncontrolled during a drag — it doesn’t snap back to value until onSlidingComplete fires
minimumValue / maximumValue number 0 / 1 Lower/upper bound of the draggable range
step number 0 Snaps the thumb to increments of this size; 0 draws implicit steps at native resolution (iOS 1000, Android 128)
lowerLimit / upperLimit number unbounded Clamps the draggable range without changing minimumValue/maximumValue
minimumTrackTintColor / maximumTrackTintColor / thumbTintColor IColorValue native default Color of the track before/after the thumb, and of the thumb itself
thumbImage / minimumTrackImage / maximumTrackImage / trackImage IImageSourceProp Replaces the corresponding native default image
thumbSize number native default Diameter of the thumb, in points
disabled boolean false Disables dragging and dims the control to the native disabled look
inverted boolean false Reverses the direction the track fills in
tapToSeek boolean false Tapping anywhere on the track jumps the thumb there, instead of requiring a drag
vertical boolean false Renders the slider top-to-bottom instead of left-to-right
renderStepNumber boolean false Draws the built-in step-number indicator
StepMarker (React) / #stepMarker (Vue) / stepMarker (Svelte) render prop / scoped slot / snippet Custom per-step marker, replacing renderStepNumber’s built-in dot
onValueChange (value: number) => void Fires continuously while dragging
onSlidingStart / onSlidingComplete (value: number) => void Fires once when a drag begins/ends
accessibility / aria-* / testID / style Pass through to the native node unchanged
  • value={0} is discarded, not applied. The shared fold treats NaN and any falsy number, including 0, as “no value given”, so the native view falls back to its own initial position — mirroring the community component. A slider that must start at zero wants minimumValue={0} and no explicit value.
  • A bare slider measures differently per platform. The iOS entry gives the wrapper a 40pt default height and nudges the step row down 10pt; the Android entry supplies no default height at all, leaving the native view’s intrinsic size to decide, and keeps the row at the top. Set an explicit height through style if the two have to agree.
  • A custom StepMarker takes over the native thumb. With a marker present, thumbImage is not forwarded to the native view — the marker draws it — and if you pass both, thumbTintColor is forced to transparent so the marker shows through.
  • Crossed limits fail quietly. lowerLimit >= upperLimit only reaches a dlog, which is off unless DEBUG is set, so an inverted pair produces no console warning at all — just a slider that won’t move where you expect.

@symbiote-native/slider ships zero runtime metadata for RNCSlider — the engine derives its events and color/image processors from the library’s own codegen ViewConfig at first commit (setNativeViewConfigSource). The package only supplies the pure JS folding the library’s React wrapper normally does (value/limit sanitizing, the step-indicator layout) plus the native Descriptor render, once, in packages/slider/src/core:

packages/slider/src/
├── core/ # framework-agnostic: state folds, render-slider, render-steps-indicator
├── register.ts # side-effect: registers RNCSlider's ViewConfig fallback + event/color processors
├── react/ # React lifecycle (hooks) + descriptorToReact bridge
├── vue/ # Vue lifecycle (refs/emits) + descriptorToVue bridge
├── angular/ # Angular lifecycle (@Output EventEmitters) + DescriptorOutlet bridge
└── svelte/ # Svelte lifecycle (runes) + the native-view bridge's descriptor-children mount

Every adapter imports ../register first (never the library’s Slider.tsx), then calls into the same core render — the same logic/view/lifecycle split as every other SymbioteNative component (see how it works). Wrapping a different third-party native view follows this same recipe: register its ViewConfig fallback and processors as a side effect, then implement the shared core render once and add a thin per-framework lifecycle bridge around it — never the library’s own React component.