---
title: createSlider
description: Headless pointer dragging, keyboard stepping, step snapping, and
  thumb styles for single or multi-thumb range sliders.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# createSlider

> Headless pointer dragging, keyboard stepping, step snapping, and thumb styles for single or multi-thumb range sliders.

`createSlider` is the headless interaction engine behind Moraine's `Slider` component. It provides pointer capture dragging, keyboard arrow stepping, value clamping, step snapping, and thumb/range inline style calculations.

## Import

```tsx
import { createSlider } from 'moraine/utils'
```

## Features

- **Single & Multi-thumb**: Supports numeric values or tuples (`[min, max]`) for dual-thumb range selection.
- **Pointer Capture**: Smooth pointer tracking that prevents cursor escape during fast mouse or touch gestures.
- **Keyboard Stepping**: Handles Arrow Left/Right/Up/Down, PageUp/PageDown, and Home/End.
- **Orientation & Inversion**: Supports horizontal or vertical orientation, as well as inverted axes and RTL.

## Signature

```ts
function createSlider<TValue extends number | number[]>(
  props: CreateSliderProps<TValue>,
  options?: CreateSliderOptions<TValue>,
): CreateSliderReturn<TValue>
```

## Usage Example

```tsx title="src/CustomSlider.tsx"
import { createSlider } from 'moraine/utils'

export function CustomSlider() {
  const slider = createSlider({
    min: 0,
    max: 100,
    step: 5,
    defaultValue: 50,
  })

  return (
    <div class="relative h-6">
      <div
        ref={(element) => slider.setTrackRef(element)}
        class="absolute inset-x-0 top-1/2 h-2 -translate-y-1/2 rounded-full bg-muted touch-none"
        onPointerDown={slider.onTrackPointerDown}
        onPointerMove={slider.onTrackPointerMove}
        onPointerUp={slider.onTrackPointerUp}
        onPointerCancel={slider.onPointerCancel}
        onLostPointerCapture={slider.onPointerCancel}
      >
        <div class="absolute h-full rounded-full bg-primary" style={slider.rangeStyle()} />
      </div>

      <div
        ref={(element) => slider.setThumbRefs(() => [element])}
        tabIndex={0}
        role="slider"
        aria-label="Amount"
        aria-valuenow={slider.currentValues()[0] ?? 0}
        aria-valuemin={0}
        aria-valuemax={100}
        onPointerDown={(event) => slider.onThumbPointerDown(0, event)}
        onPointerMove={slider.onThumbPointerMove}
        onPointerUp={slider.onThumbPointerUp}
        onPointerCancel={slider.onPointerCancel}
        onLostPointerCapture={slider.onPointerCancel}
        onKeyDown={(event) => slider.onThumbKeyDown(0, event)}
        onKeyUp={slider.onThumbKeyUp}
        style={slider.thumbStyles()[0]}
        class="absolute top-1/2 size-5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary ring-2 ring-background shadow-md touch-none"
      />
    </div>
  )
}
```

