Slider

Capture user feedback from a range of values
Import

Usage

20%
50%
80%
Type
Color
Size
xs
sm
md
lg
xl
Radius
xs
sm
md
lg
xl
import { Slider } from '@mantine/core';
function Demo() {
return (
<Slider
marks={[
{ value: 20, label: '20%' },
{ value: 50, label: '50%' },
{ value: 80, label: '80%' },
]}
/>
);
}

Controlled

By default, both Slider and RangeSlider are uncontrolled, add onChange and value props to make them controlled:

import { useState } from 'react';
import { Slider, RangeSlider } from '@mantine/core';
function Demo() {
const [value, setValue] = useState(40);
const [rangeValue, setRangeValue] = useState<[number, number]>([20, 80]);
return (
<>
<Slider value={value} onChange={setValue} />
<RangeSlider value={rangeValue} onChange={setRangeValue} />
</>
);
}

Disabled

xs
sm
md
lg
xl
import { Slider, RangeSlider } from '@mantine/core';
function Demo() {
return (
<>
<Slider defaultValue={60} disabled />
<RangeSlider
mt="xl"
mb="xl"
disabled
defaultValue={[25, 75]}
marks={[
{ value: 0, label: 'xs' },
{ value: 25, label: 'sm' },
{ value: 50, label: 'md' },
{ value: 75, label: 'lg' },
{ value: 100, label: 'xl' },
]}
/>
</>
);
}

onChangeEnd

onChangeEnd callback is called when user stops dragging the slider or uses arrows:

onChange value: 50
onChangeEnd value: 50
import { useState } from 'react';
import { Slider, Text, Container } from '@mantine/core';
function Demo() {
const [value, setValue] = useState(50);
const [endValue, setEndValue] = useState(50);
return (
<Container size={400}>
<Slider value={value} onChange={setValue} onChangeEnd={setEndValue} />
<Text mt="md" size="sm">
onChange value: <b>{value}</b>
</Text>
<Text mt={5} size="sm">
onChangeEnd value: <b>{endValue}</b>
</Text>
</Container>
);
}

Control label

To change label behavior and appearance, set the following props:

  • label – formatter function, accepts value as an argument, set null to disable label, defaults to f => f
  • labelAlwaysOn – if true – label will always be displayed, by default label is visible only when user is dragging
  • labelTransition, labelTransitionDuration, labelTransitionTimingFunction – label uses Transition component to animate presence, you can choose any premade transition or create your own
No label
Formatted label
Label always visible
40
Custom label transition
import { Slider } from '@mantine/core';
function Demo() {
return (
<>
{/* Disable label */}
<Slider label={null} />
{/* Format label with function */}
<Slider label={(value) => `${value} °C`} />
{/* Always display label */}
<Slider labelAlwaysOn />
{/* Change label transition */}
<Slider
labelTransition="skew-down"
labelTransitionDuration={150}
labelTransitionTimingFunction="ease"
/>
</>
);
}

Min, max and step

Decimal step
Step matched with marks
xs
sm
md
lg
xl
import { Slider } from '@mantine/core';
// Configure marks to match step
const MARKS = [
{ value: 0, label: 'xs' },
{ value: 25, label: 'sm' },
{ value: 50, label: 'md' },
{ value: 75, label: 'lg' },
{ value: 100, label: 'xl' },
];
function Demo() {
return (
<>
{/* Set min, max and step props to replace default values */}
<Slider
defaultValue={5}
min={-10}
max={10}
label={(value) => value.toFixed(1)}
step={0.1}
styles={{ markLabel: { display: 'none' } }}
/>
<Slider
label={(val) => MARKS.find((mark) => mark.value === val).label}
defaultValue={50}
step={25}
marks={MARKS}
styles={{ markLabel: { display: 'none' } }}
/>
</>
);
}

Marks

Add any number of marks to slider by setting marks prop to an array of objects:

const marks = [
{ value: 20 }, // -> displays mark on slider track
{ value: 40, label: '40%' }, // -> adds mark label below slider track
];

Note that mark value is relative to slider value, not width:

20%
50%
80%
20%
50%
80%
import { Slider, RangeSlider } from '@mantine/core';
const marks = [
{ value: 20, label: '20%' },
{ value: 50, label: '50%' },
{ value: 80, label: '80%' },
];
function Demo() {
return (
<>
<Slider defaultValue={40} marks={[{ value: 10 }, { value: 40 }, { value: 95 }]} />
<Slider defaultValue={40} marks={marks} />
<RangeSlider defaultValue={[20, 80]} marks={marks} />
</>
);
}

Styles API

You can change styles of any element in slider component with Styles API to match your design requirements:

20%
50%
80%
import { Slider } from '@mantine/core';
const marks = [
{ value: 20, label: '20%' },
{ value: 50, label: '50%' },
{ value: 80, label: '80%' },
];
function Demo() {
return (
<Slider
defaultValue={40}
marks={marks}
labelTransition="fade"
size={2}
styles={(theme) => ({
track: {
backgroundColor:
theme.colorScheme === 'dark' ? theme.colors.dark[3] : theme.colors.blue[1],
},
mark: {
width: 6,
height: 6,
borderRadius: 6,
transform: 'translateX(-3px) translateY(-2px)',
borderColor: theme.colorScheme === 'dark' ? theme.colors.dark[3] : theme.colors.blue[1],
},
markFilled: {
borderColor: theme.colors.blue[6],
},
markLabel: { fontSize: theme.fontSizes.xs, marginBottom: 5, marginTop: 0 },
thumb: {
height: 16,
width: 16,
backgroundColor: theme.white,
borderWidth: 1,
boxShadow: theme.shadows.sm,
},
})}
/>
);
}

Accessibility and usability

Slider and RangeSlider components are accessible by default:

  • Slider thumbs are focusable
  • When the user uses mouse to interact with a slider, focus is moved on slider track, when the user presses arrows focus is moved on thumb
  • Value can be changed with arrows with step increment/decrement

To label component for screen readers, add labels to thumbs:

<Slider thumbLabel="Thumb aria-label" />
<RangeSlider thumbFromLabel="First thumb aria-label" thumbToLabel="Second thumb aria-label" />