ExpoStartup

Slider

A customizable slider component for selecting values from a range with smooth animations.

Slider

The Slider component allows users to select a value from a range by moving a thumb along a track. It features smooth animations, customizable appearance, and support for both continuous and discrete values.

Features

  • Value Range: Supports minimum and maximum value settings
  • Smooth Animation: Fluid animation for thumb movement
  • Step Configuration: Supports continuous or stepped value selection
  • Customizable Appearance: Control for colors, sizes, and track appearance
  • Value Labels: Optional display of current value
  • Dark Mode Support: Automatic light/dark mode styles

Import

import Slider from '@/components/forms/Slider';

Props

PropTypeDefaultDescription
valuenumberCurrent value of the slider
onChange(value: number) => voidFunction called when the value changes
onSlidingComplete(value: number) => voidFunction called when sliding is completed
minimumValuenumber0Minimum value of the slider
maximumValuenumber1Maximum value of the slider
stepnumber0Step value (0 for continuous)
size's' | 'm' | 'l''m'Size of the slider
showLabelbooleanfalseWhether to show the current value label
formatLabel(value: number) => stringFunction to format the label text
classNamestring''Additional Tailwind classes for the container

Usage Examples

Basic Slider

const [value, setValue] = useState(0.5);
 
<Slider
  value={value}
  onChange={setValue}
  minimumValue={0}
  maximumValue={1}
/>

Slider with Steps

const [volume, setVolume] = useState(5);
 
<Slider
  value={volume}
  onChange={setVolume}
  minimumValue={0}
  maximumValue={10}
  step={1}
/>

Slider with Value Label

const [progress, setProgress] = useState(50);
 
<Slider
  value={progress}
  onChange={setProgress}
  minimumValue={0}
  maximumValue={100}
  showLabel={true}
  formatLabel={(value) => `${value}%`}
/>

Different Sized Sliders

<View className="space-y-8">
  <Slider
    value={value1}
    onChange={setValue1}
    size="s"
  />
  
  <Slider
    value={value2}
    onChange={setValue2}
    size="m"
  />
  
  <Slider
    value={value3}
    onChange={setValue3}
    size="l"
  />
</View>

Media Player Example

function AudioPlayer() {
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(180); // 3 minutes in seconds
  const [volume, setVolume] = useState(0.8);
 
  const formatTime = (seconds) => {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
  };
 
  const handlePlayPause = () => {
    setIsPlaying(!isPlaying);
  };
 
  return (
    <View className="p-4 bg-light-secondary dark:bg-dark-secondary rounded-lg">
      <View className="mb-6">
        <Text className="text-xl font-bold">Song Title</Text>
        <Text className="text-sm text-gray-500">Artist Name</Text>
      </View>
      
      {/* Playback progress slider */}
      <View className="mb-4">
        <Slider
          value={currentTime}
          onChange={setCurrentTime}
          onSlidingComplete={(value) => {
            setCurrentTime(value);
            // In a real app: seek to this position
          }}
          minimumValue={0}
          maximumValue={duration}
          size="m"
        />
        
        <View className="flex-row justify-between mt-1">
          <Text className="text-xs">{formatTime(currentTime)}</Text>
          <Text className="text-xs">{formatTime(duration)}</Text>
        </View>
      </View>
      
      {/* Playback controls */}
      <View className="flex-row justify-center items-center space-x-6 mb-8">
        <TouchableOpacity>
          <Icon name="SkipBack" size={24} />
        </TouchableOpacity>
        
        <TouchableOpacity 
          className="bg-highlight p-3 rounded-full" 
          onPress={handlePlayPause}
        >
          <Icon name={isPlaying ? "Pause" : "Play"} size={24} color="white" />
        </TouchableOpacity>
        
        <TouchableOpacity>
          <Icon name="SkipForward" size={24} />
        </TouchableOpacity>
      </View>
      
      {/* Volume control */}
      <View className="flex-row items-center space-x-3">
        <Icon name="Volume2" size={18} />
        <Slider
          value={volume}
          onChange={setVolume}
          minimumValue={0}
          maximumValue={1}
          size="s"
          className="flex-1"
        />
      </View>
    </View>
  );
}

Filter Range Example

function PriceRangeFilter() {
  const [priceRange, setPriceRange] = useState([20, 100]);
  
  const handleMinChange = (value) => {
    setPriceRange([value, Math.max(value, priceRange[1])]);
  };
  
  const handleMaxChange = (value) => {
    setPriceRange([Math.min(value, priceRange[0]), value]);
  };
  
  return (
    <View className="p-4 border border-gray-200 rounded-lg">
      <Text className="text-lg font-bold mb-4">Price Range Filter</Text>
      
      <View className="mb-6">
        <Text className="mb-2">Minimum Price: ${priceRange[0]}</Text>
        <Slider
          value={priceRange[0]}
          onChange={handleMinChange}
          minimumValue={0}
          maximumValue={200}
          step={5}
        />
      </View>
      
      <View className="mb-6">
        <Text className="mb-2">Maximum Price: ${priceRange[1]}</Text>
        <Slider
          value={priceRange[1]}
          onChange={handleMaxChange}
          minimumValue={0}
          maximumValue={200}
          step={5}
        />
      </View>
      
      <View className="flex-row justify-between items-center p-2 bg-light-background dark:bg-dark-background rounded">
        <Text>Selected Range:</Text>
        <Text className="font-bold">${priceRange[0]} - ${priceRange[1]}</Text>
      </View>
    </View>
  );
}

Best Practices

Appropriate Value Ranges

Choose appropriate minimum and maximum values for your use case:

// Volume slider (0 to 1 is standard for audio)
<Slider
  value={volume}
  onChange={setVolume}
  minimumValue={0}
  maximumValue={1}
  step={0.1}
/>
 
// Age selection (with meaningful range and step)
<Slider
  value={age}
  onChange={setAge}
  minimumValue={18}
  maximumValue={100}
  step={1}
  showLabel={true}
/>

Provide Visual Feedback

Show users the current value or provide context for what the slider controls:

<View>
  <View className="flex-row justify-between">
    <Text>Size Adjustment</Text>
    <Text>{size}px</Text>
  </View>
  <Slider
    value={size}
    onChange={setSize}
    minimumValue={10}
    maximumValue={50}
    step={1}
  />
  <View className="flex-row justify-between mt-1">
    <Text className="text-xs">Small</Text>
    <Text className="text-xs">Large</Text>
  </View>
</View>

Debounce for Performance

For operations that might be expensive, debounce the onChange event and use onSlidingComplete for the final value:

const [filter, setFilter] = useState(0.5);
const debouncedFilter = useDebounce(filter, 100);
 
useEffect(() => {
  // Apply filter with debounced value to avoid performance issues
  applyImageFilter(debouncedFilter);
}, [debouncedFilter]);
 
<Slider
  value={filter}
  onChange={setFilter}
  onSlidingComplete={(value) => {
    // Final value when user releases the slider
    saveUserPreference('filter', value);
  }}
  minimumValue={0}
  maximumValue={1}
/>

Implementation Details

The Slider component uses React Native's PanGestureHandler from the react-native-gesture-handler library and Animated from react-native-reanimated to create smooth and responsive interactions.

The component supports both continuous values and stepped values through the step prop. When a step value is provided (greater than 0), the slider will snap to the nearest step value as the user slides.

The component also supports different sizes through the size prop, which controls the height of the track and the size of the thumb.

Notes

  • For continuous sliders, set step to 0
  • The thumb responds to both drag gestures and taps on the track
  • When showLabel is true, the current value is displayed above the thumb
  • The formatLabel function can be used to customize the display of the current value (e.g., adding a unit or formatting as currency)
  • The component uses react-native-reanimated for performant animations

On this page