# RangeSlider

> Select a minimum and maximum with two thumbs that cannot cross.

Category: Forms · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/range-slider

Use RangeSlider to filter by a span of values, such as contract value or document age. Each thumb is its own slider with its own label, so the range is fully keyboard and screen reader accessible.

## Import

```tsx
import { RangeSlider } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Basic

```tsx
import { FormField, RangeSlider } from '@bindshq/stance-react';

export function Basic() {
  const gbp = (v: number) => `£${v}k`;
  return (
    <div style={{ width: '100%', maxWidth: 420 }}>
      <FormField label="Contract value">
        <RangeSlider
          defaultValue={[20, 80]}
          min={0}
          max={100}
          step={5}
          formatValue={gbp}
          showValue
          thumbLabels={['Minimum value', 'Maximum value']}
        />
      </FormField>
    </div>
  );
}
```

### Minimum gap

minStepsBetween keeps the thumbs apart.

```tsx
import { RangeSlider } from '@bindshq/stance-react';

export function MinimumGap() {
  return (
    <div style={{ width: '100%', maxWidth: 420 }}>
      <RangeSlider
        defaultValue={[9, 17]}
        min={0}
        max={24}
        minStepsBetween={2}
        thumbLabels={['Working hours start', 'Working hours end']}
        formatValue={(v) => `${String(v).padStart(2, '0')}:00`}
        showValue
        marks={[
          { value: 0, label: '00:00' },
          { value: 12, label: '12:00' },
          { value: 24, label: '24:00' },
        ]}
      />
    </div>
  );
}
```

### Controlled filter

```tsx
import * as React from 'react';
import { RangeSlider } from '@bindshq/stance-react';

export function Controlled() {
  const [range, setRange] = React.useState<[number, number]>([0, 30]);
  return (
    <div style={{ display: 'grid', gap: 12, width: '100%', maxWidth: 420 }}>
      <RangeSlider
        value={range}
        onValueChange={setRange}
        min={0}
        max={90}
        thumbLabels={['Newer than (days)', 'Older than (days)']}
      />
      <span style={{ fontSize: 14 }}>
        Showing documents sent {range[0]} to {range[1]} days ago
      </span>
    </div>
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| RangeSlider | `value` | `[number, number]` | None | Controlled range. |
| RangeSlider | `defaultValue` | `[number, number]` | `[min, max]` | Initial range. |
| RangeSlider | `onValueChange` | `(value: [number, number]) => void` | None | Called while dragging. |
| RangeSlider | `onValueCommit` | `(value: [number, number]) => void` | None | Called when a change ends. |
| RangeSlider | `minStepsBetween` | `number` | `0` | Minimum distance between thumbs, in steps. |
| RangeSlider | `thumbLabels` | `[string, string]` | `['Minimum', 'Maximum']` | Accessible names for the thumbs. |
| RangeSlider | `...props` | `Slider props` | None | min, max, step, marks, showValue, formatValue, orientation, size, name, disabled. |

## Accessibility

- Each thumb is a separate role="slider" with its own name from thumbLabels; give descriptive names like "Minimum value".
- Thumbs cannot pass each other: each thumb’s range is bounded by the other.
- With name set, values submit as name[0] and name[1].

## Keyboard

| Keys | Action |
| --- | --- |
| `Tab` | Moves between the two thumbs. |
| `Arrow keys` | Move the focused thumb by one step. |
| `PageUp` `PageDown` | Move by a large step. |
| `Home` `End` | Move as far as the other thumb or the bound allows. |

## Related

- https://stanceui.com/components/slider
- https://stanceui.com/components/filter-bar
