Slider

PohonGitHub
An input to select a numeric value within a range.
<script setup lang="ts">
import { ASliderRange, ASliderRoot, ASliderThumb, ASliderTrack } from 'akar';
import { ref } from 'vue';

const value = ref([50]);
</script>

<template>
  <ASliderRoot
    v-model="value"
    class="flex w-full select-none items-center relative touch-none"
    :max="100"
    :step="1"
  >
    <ASliderTrack class="rounded-full bg-background-accented grow h-[8px] relative overflow-hidden">
      <ASliderRange class="rounded-full bg-primary h-full absolute" />
    </ASliderTrack>
    <ASliderThumb
      class="rounded-full bg-background size-4 ring-2 ring-primary focus-visible:(outline-2 outline-primary/50 outline-offset-2)"
      aria-label="Volume"
    />
  </ASliderRoot>
</template>

Features

  • Can be controlled or uncontrolled.
  • Supports multiple thumbs.
  • Supports a minimum value between thumbs.
  • Supports touch or click on track to update value.
  • Supports Right to Left direction.
  • Full keyboard navigation.

Anatomy

Import all parts and piece them together.

<script setup>
import { ASliderRange, ASliderRoot, ASliderThumb, ASliderTrack } from 'akar';
</script>

<template>
  <ASliderRoot>
    <ASliderTrack>
      <ASliderRange />
    </ASliderTrack>
    <ASliderThumb />
  </ASliderRoot>
</template>

Pohon

One benefit of using Akar is its flexibility and low-level control over the components. However, this also means that you may need to manually construct more complex UI elements by combining multiple Akar components together.

If you feel there's a lot of elements that needs to be constructed manually using Akar, consider using Pohon UI instead. It provides a higher-level abstraction over Akar components with pre-defined styles and behaviors that can help you build UIs faster.

API Reference

Root

Contains all the parts of a slider. It will render an input for each thumb when used within a form to ensure events propagate correctly.

Props

Prop Default Type
as'span'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

defaultValue[0]number[]

The value of the slider when initially rendered. Use when you do not need to control the state of the slider.

dir'ltr' | 'rtl'

The reading direction of the combobox when applicable.
If omitted, inherits globally from AConfigProvider or assumes LTR (left-to-right) reading mode.

disabledfalseboolean

When true, prevents the user from interacting with the slider.

invertedfalseboolean

Whether the slider is visually inverted.

max100number

The maximum value for the range.

min0number

The minimum value for the range.

minStepsBetweenThumbs0number

The minimum permitted steps between multiple thumbs.

modelValuenumber[] | null
namestring

The name of the field. Submitted with its owning form as part of a name/value pair.

orientation'horizontal''horizontal' | 'vertical'

The orientation of the slider.

requiredboolean

When true, indicates that the user must set the value before the owning form can be submitted.

step1number

The stepping interval.

thumbAlignment'contain''contain' | 'overflow'

The alignment of the slider thumb.

  • contain: thumbs will be contained within the bounds of the track.
  • overflow: thumbs will not be bound by the track. No extra offset will be added.

Emits

Event Type
update:modelValue[payload: number[]]

Event handler called when the slider value changes

valueCommit[payload: number[]]

Event handler called when the value changes at the end of an interaction.

Useful when you only need to capture a final value e.g. to update a backend service.

Slots

Slot Type
modelValuenumber[] | null

The controlled value of the slider. Can be bind as v-model.

Data Attributes

Attribute Value
[data-disabled]Present when disabled
[data-orientation]'vertical' | 'horizontal'

Track

The track that contains the ASliderRange.

Props

Prop Default Type
as'span'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Data Attributes

Attribute Value
[data-disabled]Present when disabled
[data-orientation]'vertical' | 'horizontal'

Range

The range part. Must live inside ASliderTrack.

Props

Prop Default Type
as'span'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Data Attributes

Attribute Value
[data-disabled]Present when disabled
[data-orientation]'vertical' | 'horizontal'

Thumb

A draggable thumb. You can render multiple thumbs.

Props

Prop Default Type
as'span'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Data Attributes

Attribute Value
[data-disabled]Present when disabled
[data-orientation]'vertical' | 'horizontal'

Examples

Vertical orientation

Use the orientation prop to create a vertical slider.

// index.vue
<script setup>
import { ASliderRange, ASliderRoot, ASliderThumb, ASliderTrack } from 'akar';
</script>

<template>
  <ASliderRoot
    class="ASliderRoot"
    :default-value="[50]"
    orientation="vertical"
  >
    <ASliderTrack class="ASliderTrack">
      <ASliderRange class="ASliderRange" />
    </ASliderTrack>
    <ASliderThumb class="ASliderThumb" />
  </ASliderRoot>
</template>
/* styles.css */
.ASliderRoot {
  position: relative;
  display: flex;
  align-items: center;
}
.ASliderRoot[data-orientation='vertical'] {
  flex-direction: column;
  width: 20px;
  height: 100px;
}

.ASliderTrack {
  position: relative;
  flex-grow: 1;
  background-color: grey;
}
.ASliderTrack[data-orientation='vertical'] {
  width: 3px;
}

.ASliderRange {
  position: absolute;
  background-color: black;
}
.ASliderRange[data-orientation='vertical'] {
  width: 100%;
}

.ASliderThumb {
  display: block;
  width: 20px;
  height: 20px;
  background-color: black;
}

Create a range

Add multiple thumbs and values to create a range slider.

// index.vue
<script setup>
import { ASliderRange, ASliderRoot, ASliderThumb, ASliderTrack } from 'akar';
</script>

<template>
  <ASliderRoot :default-value="[25, 75]">
    <ASliderTrack>
      <ASliderRange />
    </ASliderTrack>
    <ASliderThumb />
    <ASliderThumb />
  </ASliderRoot>
</template>

Define step size

Use the step prop to increase the stepping interval.

// index.vue
<script setup>
import { ASliderRange, ASliderRoot, ASliderThumb, ASliderTrack } from 'akar';
</script>

<template>
  <ASliderRoot
    :default-value="[50]"
    :step="10"
  >
    <ASliderTrack>
      <ASliderRange />
    </ASliderTrack>
    <ASliderThumb />
  </ASliderRoot>
</template>

Prevent thumb overlap

Use minStepsBetweenThumbs to avoid thumbs with equal values.

// index.vue
<script setup>
import { ASliderRange, ASliderRoot, ASliderThumb, ASliderTrack } from 'akar';
</script>

<template>
  <ASliderRoot
    :default-value="[25, 75]"
    :step="10"
    :min-steps-between-thumbs="1"
  >
    <ASliderTrack>
      <ASliderRange />
    </ASliderTrack>
    <ASliderThumb />
    <ASliderThumb />
  </ASliderRoot>
</template>

Accessibility

Adheres to the ASlider WAI-ARIA design pattern.

Keyboard Interactions

Key Description
ArrowRight

Increases the value by the step amount.

ArrowLeft

Decreases the value by the step amount.

ArrowUp

Increases the value by the step amount.

ArrowDown

Decreases the value by the step amount.

PageUp

Increases the value by a larger step.

PageDown

Decreases the value by a larger step.

Shift + ArrowUp

Increases the value by a larger step.

Shift + ArrowDown

Decreases the value by a larger step.

Home

Sets the value to its minimum.

End

Sets the value to its maximum.

Inverted sliders

When the slider is inverted, some controls are inverted as well, depending on the orientation.

  • When the slider is horizontal (the default), ArrowRight, ArrowLeft, Home, and End are inverted.
  • When the slider is vertical, ArrowUp, ArrowDown, PageUp, PageDown, Shift + ArrowUp, and Shift + ArrowDown are inverted.

Custom APIs

Create your own API by abstracting the primitive parts into your own component.

Abstract all parts

This example abstracts all of the ASlider parts so it can be used as a self closing element.

Usage

<script setup lang="ts">
import { ASlider } from './your-slider';
</script>

<template>
  <ASlider :default-value="[25]" />
</template>

Implementation

// your-slider.ts
export { default as ASlider } from 'ASlider.vue';
 <!-- ASlider.vue -->
<script setup lang="ts">
import type { ASliderRootEmits, ASliderRootProps } from 'akar';
import { ASliderRange, ASliderRoot, ASliderThumb, ASliderTrack, useForwardPropsEmits } from 'akar';

const props = defineProps<ASliderRootProps>();
const emits = defineEmits<ASliderRootEmits>();

const forward = useForwardPropsEmits(props, emits);
</script>

<template>
  <ASliderRoot
    v-slot="{ modelValue }"
    v-bind="forward"
  >
    <ASliderTrack>
      <ASliderRange />
    </ASliderTrack>

    <ASliderThumb
      v-for="(_, i) in modelValue"
      :key="i"
    />
  </ASliderRoot>
</template>

Caveats

Mouse events are not fired

Because of a limitation we faced during implementation, the following example won't work as expected and the @mousedown and @mousedown event handlers won't be fired:

<ASliderRoot
  @mousedown="() => { console.log('onMouseDown')  }"
  @mouseup="() => { console.log('onMouseUp')  }"
>
</ASliderRoot>

We recommend using pointer events instead (eg. @pointerdown, @pointerup). Regardless of the above limitation, these events are better suited for cross-platform/device handling as they are fired for all pointer input types (mouse, touch, pen, etc.).