Slider
Sliders allow users to view and select a value (or range) from the range along a bar. They’re ideal for adjusting settings such as volume and brightness, or for applying image filters.
Sliders can use icons on both ends of the bar to represent a numeric or relative scale. The range of values or the nature of the values, such as volume change, can be communicated with icons.
Horizontal Slider
The Slider
is a fully controlled component requiring a value
and a
setValue
function. react-md
provides a hook to help implement this
functionality for most use cases: useSlider
.
"use client";
import { box } from "@react-md/core/box/styles";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useSlider } from "@react-md/core/form/useSlider";
import { type ReactElement, useId } from "react";
export default function HorizontalSlider(): ReactElement {
const legendId = useId();
const slider = useSlider();
// const { value, setValue, min, max, step } = slider;
return (
<Form className={box({ fullWidth: true })}>
<Fieldset fullWidth>
<Legend id={legendId}>Slider</Legend>
<Slider aria-labelledby={legendId} {...slider} />
</Fieldset>
</Form>
);
}
With Addons
Icons or other components can be rendered inline with the Slider
by passing
them to the beforeAddon
and afterAddon
props.
"use client";
import { box } from "@react-md/core/box/styles";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useSlider } from "@react-md/core/form/useSlider";
import VolumeOffOutlinedIcon from "@react-md/material-icons/VolumeOffOutlinedIcon";
import VolumeUpOutlinedIcon from "@react-md/material-icons/VolumeUpOutlinedIcon";
import { type ReactElement, useId } from "react";
export default function HorizontalSliderWithAddons(): ReactElement {
const legendId = useId();
const slider = useSlider();
// const { value, setValue, min, max, step } = slider;
return (
<Form className={box({ fullWidth: true })}>
<Fieldset fullWidth>
<Legend id={legendId}>Volume</Legend>
<Slider
aria-labelledby={legendId}
{...slider}
beforeAddon={<VolumeOffOutlinedIcon />}
afterAddon={<VolumeUpOutlinedIcon />}
/>
</Fieldset>
</Form>
);
}
Configuring useSlider
The useSlider
hook can configure the min
, max
, step
, and defaultValue
.
"use client";
import { box } from "@react-md/core/box/styles";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import {
// getRangeDefaultValue,
useSlider,
} from "@react-md/core/form/useSlider";
import { type ReactElement, useId } from "react";
// these are the defaults
const min = 0;
const max = 100;
const step = 1;
export default function ConfiguringUseSlider(): ReactElement {
const legendId = useId();
const slider = useSlider({
min,
max,
step,
defaultValue: 30,
// this is the default.. defaultValue
// defaultValue: () =>
// getRangeDefaultValue({
// min: 0,
// max: 100,
// step: 1,
// }),
});
// const { value, setValue, min, max, step } = slider;
return (
<Form className={box({ fullWidth: true })}>
<Fieldset fullWidth>
<Legend id={legendId}>Slider</Legend>
<Slider aria-labelledby={legendId} {...slider} />
</Fieldset>
</Form>
);
}
Vertical Slider
Set the vertical
prop to true
to render a Slider
vertically instead of
horizontally. The default height will be core.$slider-vertical-size (15rem
) but
can be configured by setting --rmd-slider-vertical-size
or an inline height
style.
"use client";
import { box } from "@react-md/core/box/styles";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useSlider } from "@react-md/core/form/useSlider";
import { type ReactElement, useId } from "react";
export default function VerticalSlider(): ReactElement {
const slider = useSlider();
const legendId = useId();
return (
<Form
className={box({ fullWidth: true })}
style={
{
// "--rmd-slider-vertical-size": "15rem",
}
}
>
<Fieldset fullWidth>
<Legend id={legendId}>Slider</Legend>
<Slider aria-labelledby={legendId} {...slider} vertical />
</Fieldset>
</Form>
);
}
With Addons
Icons or other components can be rendered above or below the vertical Slider
by passing them to the beforeAddon
and afterAddon
props.
"use client";
import { box } from "@react-md/core/box/styles";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useSlider } from "@react-md/core/form/useSlider";
import VolumeOffOutlinedIcon from "@react-md/material-icons/VolumeOffOutlinedIcon";
import VolumeUpOutlinedIcon from "@react-md/material-icons/VolumeUpOutlinedIcon";
import { type ReactElement, useId } from "react";
export default function VerticalSliderWithAddons(): ReactElement {
const legendId = useId();
const slider = useSlider();
// const { value, setValue, min, max, step } = slider;
return (
<Form className={box({ fullWidth: true })}>
<Fieldset fullWidth>
<Legend id={legendId}>Volume</Legend>
<Slider
aria-labelledby={legendId}
{...slider}
beforeAddon={<VolumeOffOutlinedIcon />}
afterAddon={<VolumeUpOutlinedIcon />}
vertical
/>
</Fieldset>
</Form>
);
}
Slider States
Unlike other form components, the Slider
only supports a disabled
state.
"use client";
import { box } from "@react-md/core/box/styles";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useSlider } from "@react-md/core/form/useSlider";
import { type ReactElement, useId } from "react";
export default function SliderStates(): ReactElement {
const legendId1 = useId();
const legendId2 = useId();
const slider = useSlider();
// const { value, setValue, min, max, step } = slider;
return (
<Form className={box({ fullWidth: true })}>
<Fieldset fullWidth>
<Legend id={legendId1}>Horizontal Slider</Legend>
<Slider aria-labelledby={legendId1} {...slider} disabled />
</Fieldset>
<Fieldset fullWidth>
<Legend id={legendId2}>Vertical Slider</Legend>
<Slider aria-labelledby={legendId2} {...slider} disabled vertical />
</Fieldset>
</Form>
);
}
Range Slider
The Slider
can support rendering with two thumbs that represent a range of
values. The easiest way is to control the value with the useRangeSlider
hook.
"use client";
import { box } from "@react-md/core/box/styles";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useRangeSlider } from "@react-md/core/form/useRangeSlider";
import { type ReactElement, useId } from "react";
export default function RangeSlider(): ReactElement {
const legendId = useId();
const slider = useRangeSlider();
// these are the defaults
// const min = 0;
// const max = 100;
// const step = 1;
// const slider = useRangeSlider({
// min,
// max,
// step,
// defaultValue: [min, max],
// });
// const { rangeValue, min, max, step, setRangeValue } = slider;
// const [minPrice, maxPrice] = rangeValue;
return (
<Form className={box({ fullWidth: true })}>
<Fieldset fullWidth>
<Legend id={legendId}>Slider</Legend>
<Slider aria-labelledby={legendId} {...slider} />
</Fieldset>
</Form>
);
}
Vertical Range Slider
"use client";
import { box } from "@react-md/core/box/styles";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useRangeSlider } from "@react-md/core/form/useRangeSlider";
import { type ReactElement, useId } from "react";
// these are the defaults
const min = 0;
const max = 100;
const step = 1;
export default function VerticalRangeSlider(): ReactElement {
const legendId = useId();
const slider = useRangeSlider({
min,
max,
step,
defaultValue: [20, 75],
});
// const { rangeValue, min, max, step, setRangeValue } = slider;
// const [minPrice, maxPrice] = rangeValue;
return (
<Form className={box({ fullWidth: true })}>
<Fieldset fullWidth>
<Legend id={legendId}>Slider</Legend>
<Slider aria-labelledby={legendId} {...slider} vertical />
</Fieldset>
</Form>
);
}
Discrete Slider
The current value can be shown in a tooltip by enabling the discrete
prop
which only appears while dragging or focused by default. The tooltip visibility
can be controlled by the tooltipVisibility
prop as one of the following
values:
"auto"
(default) - only appears while dragging or focused"hover"
- also appears while hovering the thumb"always"
- always visible
"use client";
import { box } from "@react-md/core/box/styles";
import { Divider } from "@react-md/core/divider/Divider";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useRangeSlider } from "@react-md/core/form/useRangeSlider";
import { useSlider } from "@react-md/core/form/useSlider";
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement, useId } from "react";
export default function DiscreteSlider(): ReactElement {
const legendId1 = useId();
const legendId2 = useId();
const legendId3 = useId();
const legendId4 = useId();
const legendId5 = useId();
const legendId6 = useId();
const slider = useSlider({ defaultValue: 30 });
const rangeSlider = useRangeSlider({ defaultValue: [30, 50] });
return (
<Form className={box({ fullWidth: true })}>
<Typography type="headline-5" margin="none">
Slider
</Typography>
<Fieldset fullWidth>
<Legend id={legendId1}>Auto</Legend>
<Slider
aria-labelledby={legendId1}
{...slider}
discrete
tooltipVisibility="auto"
/>
</Fieldset>
<Fieldset fullWidth>
<Legend id={legendId2}>Hover</Legend>
<Slider
aria-labelledby={legendId2}
{...slider}
discrete
tooltipVisibility="hover"
/>
</Fieldset>
<Fieldset fullWidth>
<Legend id={legendId3}>Always</Legend>
<Slider
aria-labelledby={legendId3}
{...slider}
discrete
tooltipVisibility="always"
/>
</Fieldset>
<Divider />
<Typography type="headline-5" margin="none">
Range Slider
</Typography>
<Fieldset fullWidth>
<Legend id={legendId4}>Auto</Legend>
<Slider
aria-labelledby={legendId4}
{...rangeSlider}
discrete
tooltipVisibility="auto"
/>
</Fieldset>
<Fieldset fullWidth>
<Legend id={legendId5}>Hover</Legend>
<Slider
aria-labelledby={legendId5}
{...rangeSlider}
discrete
tooltipVisibility="hover"
/>
</Fieldset>
<Fieldset fullWidth>
<Legend id={legendId6}>Always</Legend>
<Slider
aria-labelledby={legendId6}
{...rangeSlider}
discrete
tooltipVisibility="always"
/>
</Fieldset>
</Form>
);
}
Customizing the Discrete Slider Tooltip
The discrete slider's tooltip props can be customized by using the getTooltipProps
function and the children can be customized by the getTooltipChildren
function.
"use client";
import { box } from "@react-md/core/box/styles";
import { cssUtils } from "@react-md/core/cssUtils";
import { Fieldset } from "@react-md/core/form/Fieldset";
import { Form } from "@react-md/core/form/Form";
import { Legend } from "@react-md/core/form/Legend";
import { Slider } from "@react-md/core/form/Slider";
import { useRangeSlider } from "@react-md/core/form/useRangeSlider";
import { useSlider } from "@react-md/core/form/useSlider";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement, useId } from "react";
const vertical = false;
export default function CustomizingDiscreteSliderTooltip(): ReactElement {
const legendId1 = useId();
const legendId2 = useId();
const slider = useSlider({ min: 0, max: 10, defaultValue: 3 });
const rangeSlider = useRangeSlider({
min: 0,
max: 10,
defaultValue: [3, 5],
});
return (
<Form
className={box({
align: "stretch",
stacked: true,
fullWidth: true,
})}
>
<Fieldset fullWidth>
<Legend id={legendId1}>Slider</Legend>
<Slider
aria-labelledby={legendId1}
discrete
{...slider}
vertical={vertical}
tooltipVisibility="always"
getTooltipProps={(value) => ({
className: cssUtils({
backgroundColor:
value <= 3 ? "warning" : value >= 8 ? "error" : undefined,
}),
})}
getTooltipChildren={(value) => `${value}°`}
/>
</Fieldset>
<Fieldset fullWidth>
<Legend id={legendId2}>Range Slider</Legend>
<Slider
aria-labelledby={legendId2}
discrete
{...rangeSlider}
vertical={vertical}
tooltipVisibility="always"
getTooltipChildren={(value, isFirstThumb) => (
<span
className={cssUtils({
className: box({
disablePadding: true,
reversed: !isFirstThumb,
disableWrap: true,
}),
fontStyle: isFirstThumb ? "italic" : undefined,
fontWeight: isFirstThumb ? undefined : "bold",
})}
>
<FavoriteIcon />
{value}
</span>
)}
/>
</Fieldset>
</Form>
);
}
Slider Marks
Marks can be added to help show specific values within the Slider
by using the
marks
prop. When set to true
, a mark will be added for each step within the
range.
Customizing Slider Marks
Marks can also be defined by providing a list of specific values within the
range and an optional value for each mark. If marks need additional
customization, the getMarkLabelProps
can also be used.
Linked with a Text Field
The useSlider
hook is not required to control the state of a Slider
and can
be controlled as long as a value
and setValue
is provided. This allows the
slider to be controlled alongside a TextField
with the useNumberField
hook
or any other custom implementation.
Accessibility
The Slider
follows the slider
and range slider
guidelines. The single-thumb slider will required to be labelled with
aria-label
or aria-labelledby
while the multi-thumb slider defaults to an
aria-label
of "Min"
and "Max"
which can be configured by the
minThumbLabel
/minThumbLabelledBy
and maxThumbLabel
/maxThumbLabelledBy
props.
In addition, the current slider value can be configured using
aria-valuetext
to convert the value percentage into a human-readable text alternative using the
getValueText
prop.
Once focused with a keyboard, the value can be updated by:
- ArrowUp -> increase the value by the
step
amount - ArrowDown -> decrease the value by the
step
amount - Home -> set the value to the
min
amount - End -> set the value to the
max
amount - PageUp -> increase the value by the
jump
amount - PageDown -> decrease the value by the
jump
amount