RangeSlider
Select numeric values within a defined range using an intuitive slider interface. Perfect for volume controls, price filters, settings adjustments, and any numeric input. Built for Pulse Framework with full reactivity support.
Import
ts
import { RangeSlider } from '@odyssee/components';1
Basic Usage
Code Éditable
Résultat
With Value Display
Code Éditable
Résultat
Custom Range
Define custom minimum, maximum, and step values.
Code Éditable
Résultat
Custom Value Format
Format the displayed value with a custom function.
Code Éditable
Résultat
With Hints
Code Éditable
Résultat
Validation State
Code Éditable
Résultat
Disabled State
Code Éditable
Résultat
Reactive Slider
Control slider value with Pulse signals.
tsx
import { RangeSlider, Button, Pulse } from '@odyssee/components';
const VolumeControl = () => {
const volume = Pulse.signal(50);
const isMuted = Pulse.signal(false);
const displayVolume = Pulse.computed(() => {
return isMuted() ? 0 : volume();
});
const toggleMute = () => {
isMuted(!isMuted());
};
const increaseVolume = () => {
const current = volume();
if (current < 100) {
volume(Math.min(100, current + 10));
isMuted(false);
}
};
const decreaseVolume = () => {
const current = volume();
if (current > 0) {
volume(Math.max(0, current - 10));
}
};
return (
<div class='space-y-4'>
<RangeSlider
label='Volume Control'
value={volume}
min={0}
max={100}
showValue={true}
valueFormat={(val) => `${val}%`}
onChange={(val) => {
volume(val);
isMuted(false);
}}
disabled={isMuted()}
/>
<div class='flex gap-2'>
<Button onClick={decreaseVolume} size='sm' variant='outline'>
Volume Down
</Button>
<Button onClick={toggleMute} size='sm' variant='outline'>
{isMuted() ? 'Unmute' : 'Mute'}
</Button>
<Button onClick={increaseVolume} size='sm' variant='outline'>
Volume Up
</Button>
</div>
<div class='p-4 bg-gray-50 rounded-lg dark:bg-neutral-800'>
<p class='text-sm text-gray-700 dark:text-gray-300'>
Current Volume: {displayVolume()}%
</p>
<p class='text-xs text-gray-500 dark:text-gray-400'>
Status: {isMuted() ? 'Muted' : 'Playing'}
</p>
</div>
</div>
);
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Complete Example: Image Editor
tsx
import { RangeSlider, Card, Button, Pulse } from '@odyssee/components';
const ImageEditor = () => {
const brightness = Pulse.signal(100);
const contrast = Pulse.signal(100);
const saturation = Pulse.signal(100);
const blur = Pulse.signal(0);
const imageStyle = Pulse.computed(() => {
return {
filter: `brightness(${brightness()}%) contrast(${contrast()}%) saturate(${saturation()}%) blur(${blur()}px)`
};
});
const resetAll = () => {
brightness(100);
contrast(100);
saturation(100);
blur(0);
};
const hasChanges = Pulse.computed(() => {
return brightness() !== 100 ||
contrast() !== 100 ||
saturation() !== 100 ||
blur() !== 0;
});
return (
<Card title='Image Editor' size='lg'>
<div class='space-y-6'>
{/* Preview */}
<div class='aspect-video bg-gray-200 rounded-lg overflow-hidden dark:bg-neutral-700'>
<img
src='https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800'
alt='Preview'
class='w-full h-full object-cover'
style={imageStyle()}
/>
</div>
{/* Controls */}
<div class='space-y-4'>
<RangeSlider
label='Brightness'
value={brightness}
min={0}
max={200}
step={1}
showValue={true}
valueFormat={(val) => `${val}%`}
onChange={(val) => brightness(val)}
/>
<RangeSlider
label='Contrast'
value={contrast}
min={0}
max={200}
step={1}
showValue={true}
valueFormat={(val) => `${val}%`}
onChange={(val) => contrast(val)}
/>
<RangeSlider
label='Saturation'
value={saturation}
min={0}
max={200}
step={1}
showValue={true}
valueFormat={(val) => `${val}%`}
onChange={(val) => saturation(val)}
/>
<RangeSlider
label='Blur'
value={blur}
min={0}
max={10}
step={0.5}
showValue={true}
valueFormat={(val) => `${val}px`}
onChange={(val) => blur(val)}
/>
</div>
{/* Actions */}
<div class='flex gap-3'>
<Button
onClick={resetAll}
variant='outline'
disabled={!hasChanges()}
>
Reset All
</Button>
<Button color='primary' fullWidth>
Apply Changes
</Button>
</div>
</div>
</Card>
);
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | Signal<number> | 50 | Current slider value |
min | number | 0 | Minimum value |
max | number | 100 | Maximum value |
step | number | 1 | Step increment |
label | string | - | Slider label text |
hint | string | - | Helper text below slider |
error | string | - | Error message |
disabled | boolean | false | Disable slider |
showValue | boolean | false | Display current value |
valueFormat | (value: number) => string | - | Custom value formatter |
onChange | (value: number) => void | - | Value change callback |
name | string | - | Input name attribute |
className | string | - | Additional CSS classes |
id | string | Auto-generated | HTML id attribute |
Accessibility
The RangeSlider component follows accessibility best practices:
- ✅ Semantic HTML with native
<input type="range"> - ✅ Proper label associations
- ✅ Keyboard navigation (arrow keys, home, end)
- ✅ ARIA attributes for screen readers
- ✅ Focus indicators
- ✅ Disabled state properly communicated
tsx
// Accessibility features are built-in
const accessibleSlider = (
<RangeSlider
label='Volume'
value={50}
showValue={true}
// All ARIA and keyboard navigation handled automatically
/>
);1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Best Practices
✅ Do
- Use appropriate min/max ranges for the context
- Provide clear labels describing what the slider controls
- Show current value for important adjustments
- Use reasonable step increments
- Format values meaningfully (%, $, °C, etc.)
tsx
// Good: Clear context and formatting
const goodSlider = (
<RangeSlider
label='Product Price Filter'
min={0}
max={1000}
step={10}
value={500}
showValue={true}
valueFormat={(val) => `$${val}`}
hint='Filter products by price range'
/>
);1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
❌ Don't
- Don't use very large ranges with small steps (poor UX)
- Don't omit labels or value displays for important controls
- Don't use steps that are too large for precision
- Don't forget to validate against min/max
- Don't hide the current value for critical settings
tsx
// Bad: Unclear purpose, no formatting
const badSlider = (
<RangeSlider
value={50}
// No label
// No value display
// No formatting
/>
);
// Better: Clear and formatted
const betterSlider = (
<RangeSlider
label='Temperature'
value={20}
showValue={true}
valueFormat={(val) => `${val}°C`}
/>
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Use Cases
Volume Control
tsx
const AudioPlayer = () => {
const volume = Pulse.signal(75);
return (
<div class='p-4 bg-white rounded-lg shadow dark:bg-neutral-800'>
<RangeSlider
label='Volume'
value={volume}
min={0}
max={100}
showValue={true}
valueFormat={(val) => `${val}%`}
onChange={(val) => {
volume(val);
// Update audio element volume
document.querySelector('audio')?.volume = val / 100;
}}
/>
</div>
);
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Price Filter
tsx
const PriceFilter = () => {
const minPrice = Pulse.signal(0);
const maxPrice = Pulse.signal(1000);
return (
<div class='space-y-4'>
<h3 class='font-semibold'>Price Range</h3>
<RangeSlider
label='Minimum Price'
value={minPrice}
min={0}
max={maxPrice()}
step={10}
showValue={true}
valueFormat={(val) => `$${val}`}
onChange={(val) => minPrice(val)}
/>
<RangeSlider
label='Maximum Price'
value={maxPrice}
min={minPrice()}
max={2000}
step={10}
showValue={true}
valueFormat={(val) => `$${val}`}
onChange={(val) => maxPrice(val)}
/>
<p class='text-sm text-gray-600'>
Showing items from ${minPrice()} to ${maxPrice()}
</p>
</div>
);
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Settings Panel
tsx
const NotificationSettings = () => {
const frequency = Pulse.signal(3);
const frequencyLabels = {
1: 'Every hour',
2: 'Every 2 hours',
3: 'Every 4 hours',
4: 'Every 8 hours',
5: 'Once daily'
};
return (
<Card title='Notification Settings'>
<RangeSlider
label='Notification Frequency'
value={frequency}
min={1}
max={5}
step={1}
showValue={true}
valueFormat={(val) => frequencyLabels[val] || String(val)}
hint='How often would you like to receive notifications?'
/>
</Card>
);
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Styling & Theming
All range slider styles use Tailwind CSS and support dark mode automatically.
Custom Styling
tsx
// Custom track color (via CSS)
const customSlider = (
<div class='[&_input[type=range]]:bg-purple-200 dark:[&_input[type=range]]:bg-purple-900'>
<RangeSlider
label='Custom styled'
value={50}
/>
</div>
);1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Dark Mode
tsx
// Dark mode support is automatic
const darkModeSlider = (
<RangeSlider
label='Volume'
value={75}
showValue={true}
// Automatically uses dark:bg-gray-700, dark:text-gray-300, etc.
/>
);1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
TypeScript
Full TypeScript support with complete type definitions.
tsx
import type { RangeSliderProps } from '@odyssee/components';
import { Pulse } from '@odyssee/components';
// Type-safe props
const props: RangeSliderProps = {
label: 'Volume',
value: 50,
min: 0,
max: 100,
step: 1,
showValue: true,
valueFormat: (val: number): string => `${val}%`,
onChange: (value: number): void => {
console.log(value);
}
};
const slider = <RangeSlider {...props} />;
// Type-safe with signals
const volume = Pulse.signal<number>(75);
const typedSlider = (
<RangeSlider
label='Volume'
value={volume}
onChange={(val: number) => volume(val)}
/>
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Related Components
- Input - Text input for manual value entry
- Progress - Display progress bars
- Toggle - Binary on/off switch
- ColorPicker - Color selection
Version: 1.0.0
Last Updated: January 2025