alpha

Slider

A slider allows a user to select one or more values within a range.

installyarn add react-aria-components
version1.0.0-alpha.6
usageimport {Slider} from 'react-aria-components'

Example#


import {Label, Slider, SliderOutput, SliderThumb, SliderTrack} from 'react-aria-components';

<Slider defaultValue={30}>
  <Label>Opacity</Label>
  <SliderOutput />
  <SliderTrack>
    <SliderThumb />
  </SliderTrack>
</Slider>
import {
  Label,
  Slider,
  SliderOutput,
  SliderThumb,
  SliderTrack
} from 'react-aria-components';

<Slider defaultValue={30}>
  <Label>Opacity</Label>
  <SliderOutput />
  <SliderTrack>
    <SliderThumb />
  </SliderTrack>
</Slider>
import {
  Label,
  Slider,
  SliderOutput,
  SliderThumb,
  SliderTrack
} from 'react-aria-components';

<Slider
  defaultValue={30}
>
  <Label>
    Opacity
  </Label>
  <SliderOutput />
  <SliderTrack>
    <SliderThumb />
  </SliderTrack>
</Slider>
Show CSS
.react-aria-Slider {
  --label-color: var(--spectrum-alias-text-color);
  --track-color: gray;
  --thumb-color: slateblue;
  --thumb-color-dragging: lch(from slateblue calc(l - 10%) c h);
  --focus-ring-color: slateblue;

  display: grid;
  grid-template-areas: "label output"
                       "track track";
  grid-template-columns: 1fr auto;
  max-width: 300px;
  color: var(--label-color);

  .react-aria-Label {
    grid-area: label;
  }

  .react-aria-SliderOutput {
    grid-area: output;
  }

  .react-aria-SliderTrack {
    grid-area: track;
    position: relative;

    /* track line */
    &:before {
      content: '';
      display: block;
      position: absolute;
      background: var(--track-color);
    }
  }

  .react-aria-SliderThumb {
    width: 1.429rem;
    height: 1.429rem;
    border-radius: 50%;
    background: var(--thumb-color);
    border: 2px solid var(--spectrum-alias-background-color-default);

    &[data-dragging] {
      background: var(--thumb-color-dragging);
    }

    &[data-focus-visible] {
      box-shadow: 0 0 0 2px var(--focus-ring-color);
    }
  }

  &[data-orientation=horizontal] {
    flex-direction: column;
    width: 300px;

    .react-aria-SliderTrack {
      height: 30px;
      width: 100%;

      &:before {
        height: 3px;
        width: 100%;
        top: 50%;
        transform: translateY(-50%);
      }
    }

    .react-aria-SliderThumb {
      top: 50%;
    }
  }

  &[data-orientation=vertical] {
    height: 150px;
    display: block;

    .react-aria-Label,
    .react-aria-SliderOutput {
      display: none;
    }

    .react-aria-SliderTrack {
      width: 30px;
      height: 100%;

      &:before {
        width: 3px;
        height: 100%;
        left: 50%;
        transform: translateX(-50%);
      }
    }

    .react-aria-SliderThumb {
      left: 50%;
    }
  }

  &[data-disabled] {
    .react-aria-SliderTrack {
      opacity: 0.4;
    }
  }
}

@media (forced-colors: active) {
  .react-aria-Slider {
    forced-color-adjust: none;

    --label-color: ButtonText;
    --track-color: ButtonBorder;
    --thumb-color: ButtonBorder;
    --thumb-color-dragging: Highlight;
    --focus-ring-color: Highlight;
    --spectrum-alias-background-color-default: Canvas;

    &[data-disabled] {
      --track-color: GrayText;
      --thumb-color: GrayText;
      --label-color: GrayText;
      & .react-aria-SliderTrack {
        opacity: 1;
      }
    }
  }
}
.react-aria-Slider {
  --label-color: var(--spectrum-alias-text-color);
  --track-color: gray;
  --thumb-color: slateblue;
  --thumb-color-dragging: lch(from slateblue calc(l - 10%) c h);
  --focus-ring-color: slateblue;

  display: grid;
  grid-template-areas: "label output"
                       "track track";
  grid-template-columns: 1fr auto;
  max-width: 300px;
  color: var(--label-color);

  .react-aria-Label {
    grid-area: label;
  }

  .react-aria-SliderOutput {
    grid-area: output;
  }

  .react-aria-SliderTrack {
    grid-area: track;
    position: relative;

    /* track line */
    &:before {
      content: '';
      display: block;
      position: absolute;
      background: var(--track-color);
    }
  }

  .react-aria-SliderThumb {
    width: 1.429rem;
    height: 1.429rem;
    border-radius: 50%;
    background: var(--thumb-color);
    border: 2px solid var(--spectrum-alias-background-color-default);

    &[data-dragging] {
      background: var(--thumb-color-dragging);
    }

    &[data-focus-visible] {
      box-shadow: 0 0 0 2px var(--focus-ring-color);
    }
  }

  &[data-orientation=horizontal] {
    flex-direction: column;
    width: 300px;

    .react-aria-SliderTrack {
      height: 30px;
      width: 100%;

      &:before {
        height: 3px;
        width: 100%;
        top: 50%;
        transform: translateY(-50%);
      }
    }

    .react-aria-SliderThumb {
      top: 50%;
    }
  }

  &[data-orientation=vertical] {
    height: 150px;
    display: block;

    .react-aria-Label,
    .react-aria-SliderOutput {
      display: none;
    }

    .react-aria-SliderTrack {
      width: 30px;
      height: 100%;

      &:before {
        width: 3px;
        height: 100%;
        left: 50%;
        transform: translateX(-50%);
      }
    }

    .react-aria-SliderThumb {
      left: 50%;
    }
  }

  &[data-disabled] {
    .react-aria-SliderTrack {
      opacity: 0.4;
    }
  }
}

@media (forced-colors: active) {
  .react-aria-Slider {
    forced-color-adjust: none;

    --label-color: ButtonText;
    --track-color: ButtonBorder;
    --thumb-color: ButtonBorder;
    --thumb-color-dragging: Highlight;
    --focus-ring-color: Highlight;
    --spectrum-alias-background-color-default: Canvas;

    &[data-disabled] {
      --track-color: GrayText;
      --thumb-color: GrayText;
      --label-color: GrayText;
      & .react-aria-SliderTrack {
        opacity: 1;
      }
    }
  }
}
.react-aria-Slider {
  --label-color: var(--spectrum-alias-text-color);
  --track-color: gray;
  --thumb-color: slateblue;
  --thumb-color-dragging: lch(from slateblue calc(l - 10%) c h);
  --focus-ring-color: slateblue;

  display: grid;
  grid-template-areas: "label output"
                       "track track";
  grid-template-columns: 1fr auto;
  max-width: 300px;
  color: var(--label-color);

  .react-aria-Label {
    grid-area: label;
  }

  .react-aria-SliderOutput {
    grid-area: output;
  }

  .react-aria-SliderTrack {
    grid-area: track;
    position: relative;

    /* track line */
    &:before {
      content: '';
      display: block;
      position: absolute;
      background: var(--track-color);
    }
  }

  .react-aria-SliderThumb {
    width: 1.429rem;
    height: 1.429rem;
    border-radius: 50%;
    background: var(--thumb-color);
    border: 2px solid var(--spectrum-alias-background-color-default);

    &[data-dragging] {
      background: var(--thumb-color-dragging);
    }

    &[data-focus-visible] {
      box-shadow: 0 0 0 2px var(--focus-ring-color);
    }
  }

  &[data-orientation=horizontal] {
    flex-direction: column;
    width: 300px;

    .react-aria-SliderTrack {
      height: 30px;
      width: 100%;

      &:before {
        height: 3px;
        width: 100%;
        top: 50%;
        transform: translateY(-50%);
      }
    }

    .react-aria-SliderThumb {
      top: 50%;
    }
  }

  &[data-orientation=vertical] {
    height: 150px;
    display: block;

    .react-aria-Label,
    .react-aria-SliderOutput {
      display: none;
    }

    .react-aria-SliderTrack {
      width: 30px;
      height: 100%;

      &:before {
        width: 3px;
        height: 100%;
        left: 50%;
        transform: translateX(-50%);
      }
    }

    .react-aria-SliderThumb {
      left: 50%;
    }
  }

  &[data-disabled] {
    .react-aria-SliderTrack {
      opacity: 0.4;
    }
  }
}

@media (forced-colors: active) {
  .react-aria-Slider {
    forced-color-adjust: none;

    --label-color: ButtonText;
    --track-color: ButtonBorder;
    --thumb-color: ButtonBorder;
    --thumb-color-dragging: Highlight;
    --focus-ring-color: Highlight;
    --spectrum-alias-background-color-default: Canvas;

    &[data-disabled] {
      --track-color: GrayText;
      --thumb-color: GrayText;
      --label-color: GrayText;
      & .react-aria-SliderTrack {
        opacity: 1;
      }
    }
  }
}

Features#


The <input type="range"> HTML element can be used to build a slider, however it is very difficult to style cross browser. Slider helps achieve accessible sliders that can be styled as needed.

  • Customizable – Support for one or multiple thumbs, in both horizontal and vertical orientations. The whole slider, or individual thumbs can be disabled. Custom minimum, maximum, and step values are supported as well.
  • High quality interactions – Mouse, touch, and keyboard input is supported via the useMove hook. Pressing the track moves the nearest thumb to that position. Text selection and touch scrolling are prevented while dragging.
  • Touch friendly – Multiple thumbs or sliders can be dragged at once on multi-touch screens.
  • Accessible – Slider thumbs use visually hidden <input> elements for mobile screen reader support, and HTML form integration. <label> and <output> elements are automatically associated to provide context for assistive technologies.
  • International – Output value is formatted as a percentage or custom format according to the user's locale. The slider automatically mirrors all interactions in right-to-left languages.

Anatomy#


OutputLabelLabelLabel24TrackThumbGroup

Sliders consist of a track element showing the range of available values, one or more thumbs showing the current values, an optional <output> element displaying the current values textually, and a label. The thumbs can be dragged to allow a user to change their value. In addition, the track can be clicked to move the nearest thumb to that position.

If there is no visual label, an aria-label or aria-labelledby prop must be passed instead to identify the slider and thumbs to screen readers.

Composed components#

A Slider uses the following components, which may also be used standalone or reused in other components.

Label
A label provides context for an input element.

Props#


Slider#

NameTypeDefaultDescription
formatOptionsIntl.NumberFormatOptionsThe display format of the value label.
orientationOrientation'horizontal'The orientation of the Slider.
isDisabledbooleanWhether the whole Slider is disabled.
minValuenumber0The slider's minimum value.
maxValuenumber100The slider's maximum value.
stepnumber1The slider's step value.
valuenumbernumber[]The current value (controlled).
defaultValuenumbernumber[]The default value (uncontrolled).
labelReactNodeThe content to display as the label.
childrenReactNode( (values: SliderRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: SliderRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: SliderRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.
Events
NameTypeDefaultDescription
onChangeEnd( (value: numbernumber[] )) => voidFired when the slider stops moving, due to being let go.
onChange( (value: numbernumber[] )) => voidHandler that is called when the value changes.
Layout
NameTypeDefaultDescription
slotstringA slot name for the component. Slots allow the component to receive props from a parent component.
Accessibility
NameTypeDefaultDescription
idstringThe element's unique identifier. See MDN.
aria-labelstringDefines a string value that labels the current element.
aria-labelledbystringIdentifies the element (or elements) that labels the current element.
aria-describedbystringIdentifies the element (or elements) that describes the object.
aria-detailsstringIdentifies the element (or elements) that provide a detailed, extended description for the object.

Label#

A <Label> accepts all HTML attributes.

SliderOutput#

A <SliderOutput> renders the current value of the slider as text.

Show props
NameTypeDefaultDescription
childrenReactNode( (values: SliderRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: SliderRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: SliderRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.

SliderTrack#

The <SliderTrack> component is a grouping of one or more <SliderThumb> elements.

Show props
NameTypeDefaultDescription
childrenReactNode( (values: SliderTrackRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: SliderTrackRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: SliderTrackRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.

SliderThumb#

The <SliderThumb> component renders an individual thumb within a <SliderTrack>.

Show props
NameTypeDefaultDescription
isDisabledbooleanWhether the Thumb is disabled.
indexnumber0Index of the thumb within the slider.
autoFocusbooleanWhether the element should receive focus on render.
validationStateValidationStateWhether the input should display its "valid" or "invalid" visual styling.
isRequiredboolean

Whether user input is required on the input before form submission. Often paired with the necessityIndicator prop to add a visual indicator to the input.

labelReactNodeThe content to display as the label.
namestringThe name of the input element, used when submitting an HTML form. See MDN.
childrenReactNode( (values: SliderThumbRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: SliderThumbRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: SliderThumbRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.
Events
NameTypeDefaultDescription
onFocus( (e: FocusEvent<Target> )) => voidHandler that is called when the element receives focus.
onBlur( (e: FocusEvent<Target> )) => voidHandler that is called when the element loses focus.
onFocusChange( (isFocused: boolean )) => voidHandler that is called when the element's focus status changes.
onKeyDown( (e: KeyboardEvent )) => voidHandler that is called when a key is pressed.
onKeyUp( (e: KeyboardEvent )) => voidHandler that is called when a key is released.
Accessibility
NameTypeDefaultDescription
idstringThe element's unique identifier. See MDN.
excludeFromTabOrderboolean

Whether to exclude the element from the sequential tab order. If true, the element will not be focusable via the keyboard by tabbing. This should be avoided except in rare scenarios where an alternative means of accessing the element or its functionality via the keyboard is available.

aria-labelstringDefines a string value that labels the current element.
aria-labelledbystringIdentifies the element (or elements) that labels the current element.
aria-describedbystringIdentifies the element (or elements) that describes the object.
aria-detailsstringIdentifies the element (or elements) that provide a detailed, extended description for the object.
aria-errormessagestringIdentifies the element that provides an error message for the object.

Styling#


React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin className attribute which can be targeted using CSS selectors. These follow the react-aria-ComponentName naming convention.

.react-aria-Slider {
  /* ... */
}
.react-aria-Slider {
  /* ... */
}
.react-aria-Slider {
  /* ... */
}

A custom className can also be specified on any component. This overrides the default className provided by React Aria with your own.

<Slider className="my-slider">
  {/* ... */}
</Slider>
<Slider className="my-slider">
  {/* ... */}
</Slider>
<Slider className="my-slider">
  {/* ... */}
</Slider>

In addition, some components support multiple UI states (e.g. focused, placeholder, readonly, etc.). React Aria components expose states using DOM attributes, which you can target in CSS selectors. These are ARIA attributes wherever possible, or data attributes when a relevant ARIA attribute does not exist. For example:

.react-aria-SliderThumb[data-dragging] {
  /* ... */
}

.react-aria-SliderThumb[data-focused] {
  /* ... */
}
.react-aria-SliderThumb[data-dragging] {
  /* ... */
}

.react-aria-SliderThumb[data-focused] {
  /* ... */
}
.react-aria-SliderThumb[data-dragging] {
  /* ... */
}

.react-aria-SliderThumb[data-focused] {
  /* ... */
}

The className and style props also accept functions which receive states for styling. This lets you dynamically determine the classes or styles to apply, which is useful when using utility CSS libraries like Tailwind.

<SliderThumb
  className={({ isDragging }) => isDragging ? 'bg-gray-700' : 'bg-gray-600'}
/>
<SliderThumb
  className={({ isDragging }) =>
    isDragging ? 'bg-gray-700' : 'bg-gray-600'}
/>
<SliderThumb
  className={(
    { isDragging }
  ) =>
    isDragging
      ? 'bg-gray-700'
      : 'bg-gray-600'}
/>

Render props may also be used as children to alter what elements are rendered based on the current state. For example, you implement custom formatting for the slider's current value.

<SliderOutput>
  {state => `${state.getThumbValueLabel(0)} cookies`}
</SliderOutput>
<SliderOutput>
  {state => `${state.getThumbValueLabel(0)} cookies`}
</SliderOutput>
<SliderOutput>
  {(state) =>
    `${
      state
        .getThumbValueLabel(
          0
        )
    } cookies`}
</SliderOutput>

The states, selectors, and render props for each component used in a Slider are documented below.

Slider#

The Slider component can be targeted with the .react-aria-Slider CSS selector, or by overriding with a custom className. It supports the following states:

NameCSS SelectorDescription
orientation[data-orientation="horizontal | vertical"]The orientation of the slider.
isDisabled[data-disabled]Whether the slider is disabled.
stateState of the slider.

Label#

A Label can be targeted with the .react-aria-Label CSS selector, or by overriding with a custom className.

SliderOutput#

The SliderOutput component can be targeted with the .react-aria-SliderOutput CSS selector, or by overriding with a custom className. It supports the following states:

NameCSS SelectorDescription
orientation[data-orientation="horizontal | vertical"]The orientation of the slider.
isDisabled[data-disabled]Whether the slider is disabled.
stateState of the slider.

SliderTrack#

The SliderTrack component can be targeted with the .react-aria-SliderTrack CSS selector, or by overriding with a custom className. It supports the following states:

NameCSS SelectorDescription
isHovered[data-hovered]Whether the slider track is currently hovered with a mouse.
orientation[data-orientation="horizontal | vertical"]The orientation of the slider.
isDisabled[data-disabled]Whether the slider is disabled.
stateState of the slider.

SliderThumb#

The SliderThumb component can be targeted with the .react-aria-SliderThumb CSS selector, or by overriding with a custom className. It supports the following states:

NameCSS SelectorDescription
stateState of the slider.
isDragging[data-dragging]Whether this thumb is currently being dragged.
isHovered[data-hovered]Whether the thumb is currently hovered with a mouse.
isFocused[data-focused]Whether the thumb is currently focused.
isFocusVisible[data-focus-visible]Whether the thumb is keyboard focused.
isDisabled[data-disabled]Whether the thumb is disabled.

Reusable wrappers#


If you will use a Slider in multiple places in your app, you can wrap all of the pieces into a reusable component. This way, the DOM structure, styling code, and other logic are defined in a single place and reused everywhere to ensure consistency.

This example wraps Slider and all of its children together into a single component which accepts a label prop, which is passed to the right place. It also shows how to use the SliderTrack and SliderOutput render props to render multiple slider thumbs, depending on the provided values. When multiple thumbs are rendered, each SliderThumb should have an aria-label, which is provided via the thumbLabels prop in this example.

import type {SliderProps} from 'react-aria-components';

interface MySliderProps<T> extends SliderProps<T> {
  label?: string;
  thumbLabels?: string[];
}

function MySlider<T extends number | number[]>(
  { label, thumbLabels, ...props }: MySliderProps<T>
) {
  return (
    <Slider {...props}>
      <Label>{label}</Label>
      <SliderOutput>
        {({ state }) =>
          state.values.map((_, i) => state.getThumbValueLabel(i)).join(' – ')}
      </SliderOutput>
      <SliderTrack>
        {({ state }) =>
          state.values.map((_, i) => (
            <SliderThumb key={i} index={i} aria-label={thumbLabels?.[i]} />
          ))}
      </SliderTrack>
    </Slider>
  );
}

<MySlider
  label="Range"
  defaultValue={[30, 60]}
  thumbLabels={['start', 'end']}
/>
import type {SliderProps} from 'react-aria-components';

interface MySliderProps<T> extends SliderProps<T> {
  label?: string;
  thumbLabels?: string[];
}

function MySlider<T extends number | number[]>(
  { label, thumbLabels, ...props }: MySliderProps<T>
) {
  return (
    <Slider {...props}>
      <Label>{label}</Label>
      <SliderOutput>
        {({ state }) =>
          state.values.map((_, i) =>
            state.getThumbValueLabel(i)
          ).join(' – ')}
      </SliderOutput>
      <SliderTrack>
        {({ state }) =>
          state.values.map((_, i) => (
            <SliderThumb
              key={i}
              index={i}
              aria-label={thumbLabels?.[i]}
            />
          ))}
      </SliderTrack>
    </Slider>
  );
}

<MySlider
  label="Range"
  defaultValue={[30, 60]}
  thumbLabels={['start', 'end']}
/>
import type {SliderProps} from 'react-aria-components';

interface MySliderProps<
  T
> extends
  SliderProps<T> {
  label?: string;
  thumbLabels?: string[];
}

function MySlider<
  T extends
    | number
    | number[]
>(
  {
    label,
    thumbLabels,
    ...props
  }: MySliderProps<T>
) {
  return (
    <Slider {...props}>
      <Label>
        {label}
      </Label>
      <SliderOutput>
        {({ state }) =>
          state.values
            .map((
              _,
              i
            ) =>
              state
                .getThumbValueLabel(
                  i
                )
            ).join(
              ' – '
            )}
      </SliderOutput>
      <SliderTrack>
        {({ state }) =>
          state.values
            .map((
              _,
              i
            ) => (
              <SliderThumb
                key={i}
                index={i}
                aria-label={thumbLabels
                  ?.[i]}
              />
            ))}
      </SliderTrack>
    </Slider>
  );
}

<MySlider
  label="Range"
  defaultValue={[
    30,
    60
  ]}
  thumbLabels={[
    'start',
    'end'
  ]}
/>

Usage#


The following examples show how to use the MySlider component created in the above examples.

Vertical orientation#

Sliders are horizontally oriented by default. The orientation prop can be set to "vertical" to create a vertical slider. This example also uses aria-label rather than label to create a slider with no visible label.

<MySlider
  orientation="vertical"
  aria-label="Opacity"
  maxValue={1}
  step={0.01} />
<MySlider
  orientation="vertical"
  aria-label="Opacity"
  maxValue={1}
  step={0.01} />
<MySlider
  orientation="vertical"
  aria-label="Opacity"
  maxValue={1}
  step={0.01} />

Controlled value#

The value prop paired with the onChange event can be used to make a slider controlled. The value must fall between the Slider's minimum and maximum values, which default to 0 and 100 respectively. The onChange event receives the new slider value as a parameter, which can be used to update state.

function Example() {
  let [value, setValue] = React.useState(25);
  return (
    <>
      <MySlider<number>
        label="Cookies to buy"
        value={value}
        onChange={setValue} />
      <p>Current value: {value}</p>
    </>
  );
}
function Example() {
  let [value, setValue] = React.useState(25);
  return (
    <>
      <MySlider<number>
        label="Cookies to buy"
        value={value}
        onChange={setValue} />
      <p>Current value: {value}</p>
    </>
  );
}
function Example() {
  let [value, setValue] =
    React.useState(25);
  return (
    <>
      <MySlider<number>
        label="Cookies to buy"
        value={value}
        onChange={setValue}
      />
      <p>
        Current value:
        {' '}
        {value}
      </p>
    </>
  );
}

Multi thumb sliders specify their values as an array rather than a single number.

function Example() {
  let [value, setValue] = React.useState([25, 75]);
  return (
    <>
      <MySlider<number[]>
        label="Range"
        thumbLabels={['start', 'end']}
        value={value}
        onChange={setValue} />
      <p>Current value: {value.join(' – ')}</p>
    </>
  );
}
function Example() {
  let [value, setValue] = React.useState([25, 75]);
  return (
    <>
      <MySlider<number[]>
        label="Range"
        thumbLabels={['start', 'end']}
        value={value}
        onChange={setValue} />
      <p>Current value: {value.join(' – ')}</p>
    </>
  );
}
function Example() {
  let [value, setValue] =
    React.useState([
      25,
      75
    ]);
  return (
    <>
      <MySlider<number[]>
        label="Range"
        thumbLabels={[
          'start',
          'end'
        ]}
        value={value}
        onChange={setValue}
      />
      <p>
        Current value:
        {' '}
        {value.join(
          ' – '
        )}
      </p>
    </>
  );
}

onChangeEnd#

The onChangeEnd prop can be used to handle when a user stops dragging a slider, whereas the onChange prop is called as the user drags.

function Example() {
  let [value, setValue] = React.useState(25);
  return (
    <>
      <MySlider<number>
        label="Cookies to buy"
        defaultValue={value}
        onChangeEnd={setValue} />
      <p>Current value: {value}</p>
    </>
  );
}
function Example() {
  let [value, setValue] = React.useState(25);
  return (
    <>
      <MySlider<number>
        label="Cookies to buy"
        defaultValue={value}
        onChangeEnd={setValue} />
      <p>Current value: {value}</p>
    </>
  );
}
function Example() {
  let [value, setValue] =
    React.useState(25);
  return (
    <>
      <MySlider<number>
        label="Cookies to buy"
        defaultValue={value}
        onChangeEnd={setValue}
      />
      <p>
        Current value:
        {' '}
        {value}
      </p>
    </>
  );
}

Custom value scale#

By default, slider values are precentages between 0 and 100. A different scale can be used by setting the minValue and maxValue props.

<MySlider
  label="Cookies to buy"
  minValue={50}
  maxValue={150}
  defaultValue={100} />
<MySlider
  label="Cookies to buy"
  minValue={50}
  maxValue={150}
  defaultValue={100} />
<MySlider
  label="Cookies to buy"
  minValue={50}
  maxValue={150}
  defaultValue={100} />

Value formatting#

Values are formatted as a percentage by default, but this can be modified by using the formatOptions prop to specify a different format. formatOptions is compatible with the option parameter of Intl.NumberFormat and is applied based on the current locale.

<MySlider
  label="Currency"
  formatOptions={{style: 'currency', currency: 'JPY'}}
  defaultValue={60} />
<MySlider
  label="Currency"
  formatOptions={{style: 'currency', currency: 'JPY'}}
  defaultValue={60} />
<MySlider
  label="Currency"
  formatOptions={{
    style: 'currency',
    currency: 'JPY'
  }}
  defaultValue={60}
/>

Step values#

The step prop can be used to snap the value to certain increments. The steps are calculated starting from the minimum. For example, if minValue={2}, and step={3}, the valid step values would be 2, 5, 8, 11, etc. This example allows increments of 5 between 0 and 100.

<MySlider
  label="Amount"
  formatOptions={{style: 'currency', currency: 'USD'}}
  minValue={0}
  maxValue={100}
  step={5} />
<MySlider
  label="Amount"
  formatOptions={{style: 'currency', currency: 'USD'}}
  minValue={0}
  maxValue={100}
  step={5} />
<MySlider
  label="Amount"
  formatOptions={{
    style: 'currency',
    currency: 'USD'
  }}
  minValue={0}
  maxValue={100}
  step={5}
/>

Disabled#

A slider can be disabled using the isDisabled prop.

<MySlider
  label="Cookies to share"
  defaultValue={25}
  isDisabled />
<MySlider
  label="Cookies to share"
  defaultValue={25}
  isDisabled />
<MySlider
  label="Cookies to share"
  defaultValue={25}
  isDisabled
/>

HTML forms#

Each SliderThumb supports the name prop for integration with HTML forms.

<Slider defaultValue={50}>
  <Label>Opacity</Label>
  <SliderOutput />
  <SliderTrack>
    <SliderThumb name="opacity" />  </SliderTrack>
</Slider>
<Slider defaultValue={50}>
  <Label>Opacity</Label>
  <SliderOutput />
  <SliderTrack>
    <SliderThumb name="opacity" />  </SliderTrack>
</Slider>
<Slider
  defaultValue={50}
>
  <Label>
    Opacity
  </Label>
  <SliderOutput />
  <SliderTrack>
    <SliderThumb name="opacity" />  </SliderTrack>
</Slider>

Advanced customization#


Composition#

If you need to customize one of the components within a Slider, such as Label or SliderOutput, in many cases you can create a wrapper component. This lets you customize the props passed to the component.

function MySliderOutput(props) {
  return <MySliderOutput {...props} className="my-slider-output" />
}
function MySliderOutput(props) {
  return (
    <MySliderOutput
      {...props}
      className="my-slider-output"
    />
  );
}
function MySliderOutput(
  props
) {
  return (
    <MySliderOutput
      {...props}
      className="my-slider-output"
    />
  );
}

Hooks#

If you need to customize things even further, such as accessing internal state or customizing DOM structure, you can drop down to the lower level Hook-based API. See useSlider for more details.