Skip to main content

Range Slider

A flexible and intuitive slider component for selecting values from a range. Built on top of rc-slider, the RangeSlider component seamlessly integrates with RizzUI's design system, providing a consistent and accessible way to capture numeric input through visual interaction.

Features

  • 🎚️ Range Selection - Single value or dual-handle range selection
  • 📏 Multiple Sizes - Three size options (sm, md, lg) to match your design
  • 🎯 Precise Control - Support for steps and marks for guided selection
  • 📊 Customizable Marks - Add labels at specific points along the slider
  • 🎨 Smooth Interactions - Smooth dragging with visual feedback
  • Accessible - Built with accessibility best practices
  • 🎯 Type Safe - Full TypeScript support
  • 📝 Form Integration - Works seamlessly with form libraries

Installation


Before using the RangeSlider component, you'll need to install the required dependency:

Step 1

Install the rc-slider package.

npm install rc-slider

Step 2

Create a range slider component, components/range-slider.tsx

import React from 'react';
import { tv, type VariantProps } from 'tailwind-variants';
import { cn } from 'rizzui/cn';
import Slider from 'rc-slider';
import type { SliderProps } from 'rc-slider';
import 'rc-slider/assets/index.css';

const rangeSlider = tv({
slots: {
container:
'[&>.rc-slider-rail]:!bg-gray-200 [&>.rc-slider-handle]:!opacity-100 [&>.rc-slider-handle-dragging]:!shadow-none [&>.rc-slider-handle-dragging]:!ring-4 [&>.rc-slider-track]:!bg-primary [&>.rc-slider-handle]:!border-primary-dark [&>.rc-slider-handle]:hover:!border-primary-dark [&>.rc-slider-handle-dragging]:!border-primary-dark [&>.rc-slider-handle-dragging]:!ring-primary/40 [&>.rc-slider-step>.rc-slider-dot-active]:!border-primary-dark',
},
variants: {
size: {
sm: {
container:
'[&>.rc-slider-rail]:!h-0.5 [&>.rc-slider-track]:!h-0.5 [&>.rc-slider-handle]:!h-3 [&>.rc-slider-handle]:!w-3 [&>.rc-slider-handle]:!border-[3px]',
},
md: {
container:
'[&>.rc-slider-rail]:!h-1 [&>.rc-slider-track]:!h-1 [&>.rc-slider-handle]:!h-4 [&>.rc-slider-handle]:!w-4 [&>.rc-slider-handle]:!border-4 [&>.rc-slider-handle]:!-mt-1.5',
},
lg: {
container:
'[&>.rc-slider-rail]:!h-2 [&>.rc-slider-track]:!h-2 [&>.rc-slider-handle]:!h-5 [&>.rc-slider-handle]:!w-5 [&>.rc-slider-handle]:!border-[5px] [&>.rc-slider-handle]:!-mt-1.5',
},
},
},
defaultVariants: {
size: 'md',
},
});

export interface RangeSliderProps extends SliderProps {
size?: VariantProps<typeof rangeSlider>['size'];
}

export default function RangeSlider({
size = 'md',
className,
...props
}: RangeSliderProps) {
const { container: containerClass } = rangeSlider({ size });

return <Slider className={cn(containerClass(), className)} {...props} />;
}

Usage


Basic Example

The simplest way to use the RangeSlider component with default settings:

import RangeSlider from '@components/range-slider';

export default function App() {
return <RangeSlider defaultValue={20} />;
}

Controlled Component

For controlled usage, manage the slider value with state:

import React from 'react';
import RangeSlider from '@components/range-slider';

export default function App() {
const [value, setValue] = React.useState(20);

return (
<RangeSlider
value={value}
onChange={(newValue) => setValue(newValue as number)}
/>
);
}

Sizes

Choose from three size options to match your design requirements:

import RangeSlider from '@components/range-slider';

export default function App() {
return (
<>
<RangeSlider size="sm" defaultValue={20} />
<RangeSlider defaultValue={25} />
<RangeSlider size="lg" defaultValue={30} />
</>
);
}

Range Mode

Enable dual-handle mode to select a range of values:

import RangeSlider from '@components/range-slider';

export default function App() {
return <RangeSlider defaultValue={[20, 60]} range />;
}

With Marks & Steps

Add marks and steps to guide users through specific values:

0200040006000800010000
import RangeSlider from '@components/range-slider';

export default function App() {
return (
<RangeSlider
marks={{
0: '0',
20: '2000',
40: '4000',
60: '6000',
80: '8000',
100: '10000',
}}
step={20}
defaultValue={40}
/>
);
}

Range with Marks & Steps

Combine range mode with marks and steps for advanced range selection:

0200040006000800010000
import RangeSlider from '@components/range-slider';

export default function App() {
return (
<RangeSlider
range
marks={{
0: '0',
20: '2000',
40: '4000',
60: '6000',
80: '8000',
100: '10000',
}}
step={20}
defaultValue={[20, 60]}
/>
);
}

With Range Counter

Create a custom range slider with input fields for min and max values:

import React from 'react';
import RangeSlider from '@components/range-slider';
import { cn } from 'rizzui/cn';

export default function App() {
const [state, setState] = React.useState({
min: 200,
max: 600,
});

function handleRangeChange(value: any) {
setState({
min: value[0],
max: value[1],
});
}
function handleMaxChange(max: number) {
setState({
...state,
max: max || state.min,
});
}
function handleMinChange(min: number) {
setState({
...state,
min: min || 0,
});
}

return (
<div>
<RangeSlider
range
min={0}
max={1000}
value={[state.min, state.max]}
onChange={(value: any) => handleRangeChange(value)}
className={cn('[&>.rc-slider-step]:hidden')}
/>
<div className="mt-5 flex items-center justify-between gap-5 text-sm font-bold">
<div className="overflow-hidden rounded-lg max-w-[200px] w-full border border-gray-200 py-2">
<p className="px-3 pt-1 text-gray-400">Min</p>
<input
type="number"
value={state.min}
onChange={(e) => handleMinChange(parseInt(e.target.value))}
className="w-full border-none !bg-gray-lightest p-3 pb-1 text-sm outline-none focus:shadow-none focus:ring-0"
min={0}
max={state.max}
readOnly
/>
</div>
<div className="overflow-hidden rounded-lg max-w-[200px] w-full border border-gray-200 py-2">
<p className="px-3 pt-1 text-gray-400">Max</p>
<input
type="number"
value={state.max}
onChange={(e) => handleMaxChange(parseInt(e.target.value))}
className="w-full border-none !bg-gray-lightest p-3 pb-1 text-sm outline-none focus:shadow-none focus:ring-0"
min={state.min}
readOnly
/>
</div>
</div>
</div>
);
}

Disabled State

Disable the slider to prevent user interaction:

import RangeSlider from '@components/range-slider';

export default function App() {
return <RangeSlider defaultValue={30} disabled />;
}

Advanced Usage

Vertical Slider

Display the slider vertically:

import RangeSlider from '@components/range-slider';

export default function App() {
return <RangeSlider vertical defaultValue={30} style={{ height: 200 }} />;
}

Custom Min/Max Values

Set custom minimum and maximum values:

import RangeSlider from '@components/range-slider';

export default function App() {
return <RangeSlider min={10} max={100} defaultValue={50} />;
}

Custom Step Size

Control the increment value:

import RangeSlider from '@components/range-slider';

export default function App() {
return <RangeSlider min={0} max={100} step={5} defaultValue={25} />;
}

Controlled Range

Manage range values with state:

import React from 'react';
import RangeSlider from '@components/range-slider';

export default function App() {
const [range, setRange] = React.useState([20, 80]);

return (
<RangeSlider
range
value={range}
onChange={(value) => setRange(value as number[])}
/>
);
}

API Reference


RangeSlider Props

PropTypeDescriptionDefault
size"sm" | "md" | "lg"Size of the slider component"md"
valuenumber | number[]Current slider value(s) (controlled)undefined
defaultValuenumber | number[]Default slider value(s) (uncontrolled)undefined
rangebooleanEnable dual-handle range modefalse
minnumberMinimum value0
maxnumberMaximum value100
stepnumber | nullStep size for value changes1
marksRecord<string | number, ReactNode | MarkObj>Marks to display on the sliderundefined
disabledbooleanDisable all slider interactionsfalse
verticalbooleanDisplay slider verticallyfalse
reversebooleanReverse the slider directionfalse
allowCrossbooleanAllow handles to cross in range modetrue
pushablenumber | booleanMinimum distance between handles in range modefalse
draggableTrackbooleanAllow dragging the track in range modefalse
onChange(value: number | number[]) => voidCallback when value changesundefined
onBeforeChange(value: number | number[]) => voidCallback before value changesundefined
onAfterChange(value: number | number[]) => voidCallback after value changesundefined
classNamestringAdditional CSS classes for the sliderundefined
styleCSSPropertiesInline styles for the sliderundefined

Note: The RangeSlider component extends all props from rc-slider, allowing you to use features like handleRender, trackStyle, handleStyle, railStyle, and more. Refer to the rc-slider documentation for a complete list of available props.

RangeSlider Sizes

type RangeSliderSizes = 'sm' | 'md' | 'lg';
  • sm - Small size with compact handle (12px × 12px)
  • md - Medium size with standard handle (16px × 16px) - default
  • lg - Large size with larger handle (20px × 20px)

Best Practices

  • Set appropriate min/max - Always define meaningful min and max values for your use case
  • Use steps - Provide step values for discrete selections when appropriate
  • Add marks - Use marks to guide users and show important value points
  • Handle onChange - Use controlled components for form integration
  • Consider range mode - Use range mode when users need to select a range of values
  • Accessibility - The component includes built-in ARIA attributes for screen readers
  • Mobile considerations - Ensure touch targets are large enough for mobile users
  • Visual feedback - The component provides visual feedback during dragging