Skip to content

Range

Lets users select a numeric value, or a range between two values, by dragging a handle along a track. Supports horizontal and vertical orientations, step snapping, optional value tooltips, and binds through x-model. The slider wraps a native input, so the value participates in forms and validation.

Usage

Use range sliders for selecting numeric values within a defined range, such as volume, price, or time intervals. Avoid using sliders for exact numeric input as precision can be difficult with dragging alone. For precise values, pair the slider with a visible readout or an Input.

The element must contain a native <input> as a direct child. The slider hides it and keeps its value in sync (low,high in dual mode), while the input carries everything form-related: the submitted name, the disabled state, constraints, custom validity, and aria-invalid. A form reset restores the slider to its initial values.

Keyboard Handling

Each handle is focusable and supports:

  • Right / Up - Increase the value by one step (Right and Left swap in right-to-left layouts).
  • Left / Down - Decrease the value by one step.
  • Page Up / Page Down - Increase or decrease the value by ten steps.
  • Home - Set the minimum value.
  • End - Set the maximum value.

In dual mode the two handles cannot cross, so Home on the upper handle stops at the lower handle's value and End on the lower handle stops at the upper handle's value.

Accessibility

Every handle exposes role="slider" with aria-valuemin / aria-valuemax / aria-valuenow and aria-orientation. When data-unit is set, a matching aria-valuetext (e.g. "42%") is announced as well. In dual mode the element itself becomes a role="group" named "Range" by default, which you can override with your own aria-label or aria-labelledby, and the handles receive the data-min-label / data-max-label names. While the inner input is invalid (or carries aria-invalid), the handles are marked aria-invalid so the state is announced. Pointer dragging, track clicks, and full keyboard operation are all supported, and pressing the track moves the nearest handle.

API Reference

Component attribute(s)

x-h-range

Attributes

AttributeValuesRequiredDescription
data-minnumberfalseLowest selectable value (default: 0).
data-maxnumberfalseHighest selectable value (default: 100).
data-stepnumberfalseStep the values snap to (default: 1).
data-valuenumberfalseInitial value when no x-model is bound. In dual mode, two comma-separated values (e.g. "20,80").
data-tooltipstrue
auto
falseShows the current value in a tooltip at each handle. With auto, only while a handle is dragged or focused.
data-unitstringfalseSuffix appended to tooltip text and aria-valuetext (e.g. "%").
data-labelstringfalseAccessible name for the handle in single mode (Defaults to "Value"). Ignored in dual mode.
data-min-labelstringfalseAccessible name for the lower handle in dual mode (default: "Minimum").
data-max-labelstringfalseAccessible name for the upper handle in dual mode (default: "Maximum").

Modifiers

ModifierDescription
verticalRenders a vertical slider. Give the element a height.
dualRenders two handles for selecting a range between two values.

Model

Bind a number with x-model, or an array of two numbers ([low, high]) in dual mode. The model updates live while a handle is dragged. Values coming from the model are clamped to the data-min / data-max bounds and snapped to data-step for display.

Events

EventDescription
inputFired continuously while a handle moves. The current value is in event.detail.
changeFired when a handle is released or after a keyboard change. The final value is in event.detail.

Validation timing

By default this control shows native-constraint errors only after the user attempts to submit the form, since the inner input is never edited directly. To validate on load instead, set data-validate="immediate" on a wrapping x-h-fieldset, x-h-field, or any ancestor element. Setting aria-invalid="true" on the inner input always shows the error immediately. See Fieldset for details.

Examples

Default

html
<div x-data="{ volume: 40 }" class="flex items-center gap-4">
  <div x-h-range x-model="volume" data-label="Volume">
    <input type="text" name="volume" />
  </div>
  <span class="w-12 text-right" x-text="volume"></span>
</div>

Dual handles

html
<div x-data="{ price: [20, 80] }" class="flex items-center gap-4">
  <div x-h-range.dual x-model="price" aria-label="Price range">
    <input type="text" name="price" />
  </div>
  <span class="w-12 text-right" x-text="price[0] + '-' + price[1]"></span>
</div>

Vertical

html
<div x-h-range.vertical data-value="30" data-label="Balance" style="height: 10rem">
  <input type="text" name="balance" />
</div>
<div x-h-range.vertical.dual data-value="20,60" aria-label="Working hours" style="height: 10rem">
  <input type="text" name="hours" />
</div>

Tooltips

Set data-tooltips="true" for always-visible value tooltips, or data-tooltips="auto" to show them only while a handle is dragged or focused.

html
<div x-h-range data-value="40" data-tooltips="true" data-unit="%" data-label="Humidity">
  <input type="text" name="humidity" />
</div>
<div x-h-range.dual data-value="20,80" data-tooltips="auto" data-unit="%" aria-label="Brightness range">
  <input type="text" name="brightness" />
</div>

Steps and bounds

html
<div x-data="{ rating: 6 }" class="flex items-center gap-4">
  <div x-h-range x-model="rating" data-min="1" data-max="8" data-step="1" data-label="Rating">
    <input type="text" name="rating" />
  </div>
  <span class="w-12 text-right" x-text="rating"></span>
</div>
<div x-data="{ opacity: 0.5 }" class="flex items-center gap-4">
  <div x-h-range x-model="opacity" data-min="0" data-max="1" data-step="0.1" data-label="Opacity">
    <input type="text" name="opacity" />
  </div>
  <span class="w-12 text-right" x-text="opacity"></span>
</div>

Invalid

Reacts to the native invalid state or to the aria-invalid attribute set on the inner input. Native errors appear after a submit attempt: drag the second slider above 50 and press Submit, then drag it back to clear the error.

html
<div x-h-range data-value="40" data-label="Volume">
  <input type="text" name="volume" aria-invalid="true" />
</div>
<form x-data @submit.prevent class="flex flex-col items-start gap-4">
  <div x-h-range data-value="40" data-label="Budget" @input="$refs.budget.setCustomValidity($event.detail > 50 ? 'Keep the budget at 50 or below' : '')">
    <input type="text" name="budget" x-ref="budget" />
  </div>
  <button x-h-button type="submit">Submit</button>
</form>

Disabled

Set the native disabled attribute on the inner input to disable the slider.

html
<div x-h-range data-value="40" data-label="Volume">
  <input type="text" name="volume" disabled />
</div>