Sliders allow users to make selections from a range of values.
Sliders reflect a range of values along a bar, from which users may select a single value. They are ideal for adjusting settings such as volume, brightness, or applying image filters.
Continuous sliders allow users to select a value along a subjective range.
{{ādemoā: āContinuousSlider.jsā}}
For smaller slider, use the prop size="small"
.
{{ādemoā: āSliderSizes.jsā}}
Discrete sliders can be adjusted to a specific value by referencing its value indicator.
You can generate a mark for each step with marks={true}
.
{{ādemoā: āDiscreteSlider.jsā}}
You can change the default step increment.
Make sure to adjust the shiftStep
prop (the granularity with which the slider can step when using Page Up/Down or Shift + Arrow Up/Down) to a value divadable with the step
.
{{ādemoā: āDiscreteSliderSteps.jsā}}
You can have custom marks by providing a rich array to the marks
prop.
{{ādemoā: āDiscreteSliderMarks.jsā}}
You can restrict the selectable values to those provided with the marks
prop with step={null}
.
{{ādemoā: āDiscreteSliderValues.jsā}}
You can force the thumb label to be always visible with valueLabelDisplay="on"
.
{{ādemoā: āDiscreteSliderLabel.jsā}}
The slider can be used to set the start and end of a range by supplying an array of values to the value
prop.
{{ādemoā: āRangeSlider.jsā}}
You can enforce a minimum distance between values in the onChange
event handler.
By default, when you move the pointer over a thumb while dragging another thumb, the active thumb will swap to the hovered thumb. You can disable this behavior with the disableSwap
prop.
If you want the range to shift when reaching minimum distance, you can utilize the activeThumb
parameter in onChange
.
{{ādemoā: āMinimumDistanceSlider.jsā}}
In this example, an input allows a discrete value to be set.
{{ādemoā: āInputSlider.jsā}}
{{ādemoā: āColorSlider.jsā}}
Here are some examples of customizing the component. You can learn more about this in the overrides documentation page.
{{ādemoā: āCustomizedSlider.jsā}}
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Slider from '@mui/material/Slider';
import IconButton from '@mui/material/IconButton';
import Stack from '@mui/material/Stack';
import PauseRounded from '@mui/icons-material/PauseRounded';
import PlayArrowRounded from '@mui/icons-material/PlayArrowRounded';
import FastForwardRounded from '@mui/icons-material/FastForwardRounded';
import FastRewindRounded from '@mui/icons-material/FastRewindRounded';
import VolumeUpRounded from '@mui/icons-material/VolumeUpRounded';
import VolumeDownRounded from '@mui/icons-material/VolumeDownRounded';
const WallPaper = styled('div')({
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0,
overflow: 'hidden',
background: 'linear-gradient(rgb(255, 38, 142) 0%, rgb(255, 105, 79) 100%)',
transition: 'all 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275) 0s',
'&::before': {
content: '""',
width: '140%',
height: '140%',
position: 'absolute',
top: '-40%',
right: '-50%',
background:
'radial-gradient(at center center, rgb(62, 79, 249) 0%, rgba(62, 79, 249, 0) 64%)',
},
'&::after': {
content: '""',
width: '140%',
height: '140%',
position: 'absolute',
bottom: '-50%',
left: '-30%',
background:
'radial-gradient(at center center, rgb(247, 237, 225) 0%, rgba(247, 237, 225, 0) 70%)',
transform: 'rotate(30deg)',
},
});
const Widget = styled('div')(({ theme }) => ({
padding: 16,
borderRadius: 16,
width: 343,
maxWidth: '100%',
margin: 'auto',
position: 'relative',
zIndex: 1,
backgroundColor: 'rgba(255,255,255,0.4)',
backdropFilter: 'blur(40px)',
...theme.applyStyles('dark', {
backgroundColor: 'rgba(0,0,0,0.6)',
}),
}));
const CoverImage = styled('div')({
width: 100,
height: 100,
objectFit: 'cover',
overflow: 'hidden',
flexShrink: 0,
borderRadius: 8,
backgroundColor: 'rgba(0,0,0,0.08)',
'& > img': {
width: '100%',
},
});
const TinyText = styled(Typography)({
fontSize: '0.75rem',
opacity: 0.38,
fontWeight: 500,
letterSpacing: 0.2,
});
export default function MusicPlayerSlider() {
const duration = 200; // seconds
const [position, setPosition] = React.useState(32);
const [paused, setPaused] = React.useState(false);
function formatDuration(value: number) {
const minute = Math.floor(value / 60);
const secondLeft = value - minute * 60;
return `${minute}:${secondLeft < 10 ? `0${secondLeft}` : secondLeft}`;
}
return (
<Box sx={{ width: '100%', overflow: 'hidden', position: 'relative', p: 3 }}>
<Widget>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<CoverImage>
<img
alt="can't win - Chilling Sunday"
src="/static/images/sliders/chilling-sunday.jpg"
/>
</CoverImage>
<Box sx={{ ml: 1.5, minWidth: 0 }}>
<Typography
variant="caption"
sx={{ color: 'text.secondary', fontWeight: 500 }}
>
Jun Pulse
</Typography>
<Typography noWrap>
<b>ąøąøą¹ąøą¹ąø²ą¹ąøąø²ąøąø³ą¹ąø§ą¹ąøąøµ (Can't win)</b>
</Typography>
<Typography noWrap sx={{ letterSpacing: -0.25 }}>
Chilling Sunday — ąøąøą¹ąøą¹ąø²ą¹ąøąø²ąøąø³ą¹ąø§ą¹ąøąøµ
</Typography>
</Box>
</Box>
<Slider
aria-label="time-indicator"
size="small"
value={position}
min={0}
step={1}
max={duration}
onChange={(_, value) => setPosition(value as number)}
sx={(t) => ({
color: 'rgba(0,0,0,0.87)',
height: 4,
'& .MuiSlider-thumb': {
width: 8,
height: 8,
transition: '0.3s cubic-bezier(.47,1.64,.41,.8)',
'&::before': {
boxShadow: '0 2px 12px 0 rgba(0,0,0,0.4)',
},
'&:hover, &.Mui-focusVisible': {
boxShadow: `0px 0px 0px 8px ${'rgb(0 0 0 / 16%)'}`,
...t.applyStyles('dark', {
boxShadow: `0px 0px 0px 8px ${'rgb(255 255 255 / 16%)'}`,
}),
},
'&.Mui-active': {
width: 20,
height: 20,
},
},
'& .MuiSlider-rail': {
opacity: 0.28,
},
...t.applyStyles('dark', {
color: '#fff',
}),
})}
/>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mt: -2,
}}
>
<TinyText>{formatDuration(position)}</TinyText>
<TinyText>-{formatDuration(duration - position)}</TinyText>
</Box>
<Box
sx={(theme) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mt: -1,
'& svg': {
color: '#000',
...theme.applyStyles('dark', {
color: '#fff',
}),
},
})}
>
<IconButton aria-label="previous song">
<FastRewindRounded fontSize="large" />
</IconButton>
<IconButton
aria-label={paused ? 'play' : 'pause'}
onClick={() => setPaused(!paused)}
>
{paused ? (
<PlayArrowRounded sx={{ fontSize: '3rem' }} />
) : (
<PauseRounded sx={{ fontSize: '3rem' }} />
)}
</IconButton>
<IconButton aria-label="next song">
<FastForwardRounded fontSize="large" />
</IconButton>
</Box>
<Stack
spacing={2}
direction="row"
sx={(theme) => ({
mb: 1,
px: 1,
'& > svg': {
color: 'rgba(0,0,0,0.4)',
...theme.applyStyles('dark', {
color: 'rgba(255,255,255,0.4)',
}),
},
})}
alignItems="center"
>
<VolumeDownRounded />
<Slider
aria-label="Volume"
defaultValue={30}
sx={(t) => ({
color: 'rgba(0,0,0,0.87)',
'& .MuiSlider-track': {
border: 'none',
},
'& .MuiSlider-thumb': {
width: 24,
height: 24,
backgroundColor: '#fff',
'&::before': {
boxShadow: '0 4px 8px rgba(0,0,0,0.4)',
},
'&:hover, &.Mui-focusVisible, &.Mui-active': {
boxShadow: 'none',
},
},
...t.applyStyles('dark', {
color: '#fff',
}),
})}
/>
<VolumeUpRounded />
</Stack>
</Widget>
<WallPaper />
</Box>
);
}
Set the orientation
prop to "vertical"
to create vertical sliders. The thumb will track vertical movement instead of horizontal movement.
{{ādemoā: āVerticalSlider.jsā}}
Chrome versions below 124 implement
aria-orientation
incorrectly for vertical sliders and expose them as'horizontal'
in the accessibility tree. (Chromium issue #40736841)The
-webkit-appearance: slider-vertical
CSS property can be used to correct this for these older versions, with the trade-off of causing a console warning in newer Chrome versions:
.MuiSlider-thumb input { -webkit-appearance: slider-vertical; }
You can customize your slider by adding and repositioning marks for minimum and maximum values.
{{ādemoā: āCustomMarks.jsā}}
The track shows the range available for user selection.
The track can be turned off with track={false}
.
{{ādemoā: āTrackFalseSlider.jsā}}
The track can be inverted with track="inverted"
.
{{ādemoā: āTrackInvertedSlider.jsā}}
You can use the scale
prop to represent the value
on a different scale.
In the following demo, the value x represents the value 2^x. Increasing x by one increases the represented value by factor 2.
{{ādemoā: āNonLinearSlider.jsā}}
(WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/slider-multithumb/)
The component handles most of the work necessary to make it accessible. However, you need to make sure that:
aria-label
, aria-labelledby
or getAriaLabel
prop).getAriaValueText
or aria-valuetext
prop.