API Reference
Anatomy
Root
Renders a div. Holds the value, the gestures, and the CSS variables.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | number | — | Controlled value. The accepted prop determines the value and settled progress. |
| defaultValue | number | min | Starting value when uncontrolled. |
| min | number | 0 | Lower bound. |
| max | number | 100 | Upper bound. |
| step | number | 1 | Granularity of emitted values. Also the arrow-key step. Both bounds remain reachable even when off the step grid. |
| unitsPerTurn | number | 100 | The gearing: how far one full revolution moves the value. |
| detent | number | 0 | Units between detents. onTick fires for every crossing, including multiple crossings in one update. 0 disables detents. |
| haptics | boolean | true | Pulse the motor when detents are crossed, where supported. Multiple crossings in one update share a pulse. |
| inertia | boolean | false | Continue spinning after release. Touch resumes dragging; scroll and keyboard input stop coasting. |
| decelerationRate | number | 0.998 | Velocity retained per millisecond while coasting. Lower values stop sooner. |
| disabled | boolean | false | Inert. Cancels an active interaction without committing, sets data-disabled on every part, and omits the hidden input from form submission. |
| name | string | — | Renders a hidden input with this name, for forms. |
| onValueChange | (value: number, details: ChangeDetails) => void | — | Fires on every change while dragging, coasting, scrolling or keying. |
| onValueCommitted | (value: number, details: ChangeDetails) => void | — | Fires once when an interaction completes, even if the value did not change. With inertia, waits until the wheel settles. Cancelled interactions do not commit. |
| onInteractionChange | (active: boolean, details: InteractionDetails) => void | — | Brackets pointer, scroll and keyboard interactions, including coasting. Always ends, including cancellation and unmount. |
| onDraggingChange | (dragging: boolean) => void | — | Reports only when a pointer takes or releases the ring. Use onInteractionChange to track the full interaction, including coasting, scroll and keyboard input. |
| onTick | (direction: 1 | -1) => void | — | Fires per detent crossing, with the direction of travel. |
Interaction lifecycle
ClickWheel.ChangeDetails contains source: pointer, wheel, or keyboard. ClickWheel.InteractionDetails also contains cancelled. An interaction starts with onInteractionChange(true), emits value changes, commits once, then ends with onInteractionChange(false). A pointer interaction includes its coast; grabbing that coast continues the same interaction. Scroll events are grouped until 150 ms of inactivity; each keyboard action commits immediately.
Disabling, pointer cancellation, losing capture, window blur during a drag, or unmounting ends the interaction with cancelled: true and no commit. Already accepted values remain. Use onInteractionChange to suspend and resume playback-driven updates, and onValueCommitted to seek. onDraggingChange only reports whether a pointer holds the ring.
Ring
Renders a div with role="slider". Pointer, scroll and keyboard input live here.
| Prop | Type | Default | Description |
|---|---|---|---|
| aria-label | string | — | The accessible name. The ring is the slider. |
| getAriaValueText | (value: number) => string | — | Formats the value for screen readers, e.g. seconds to “1 min 20 sec”. |
Rotor
Renders an aria-hidden div with rotate: var(--click-wheel-rotation). Rotates with pointer movement. Has no additional props.
Center
Renders a button with type=button, including when composed with render. An explicit type=submit is respected. Pass onClick and an aria-label, or render a div for a plain hub. A disabled Root prevents Center activation even when a custom element overrides its disabled prop.
Common props
Every part accepts these, plus the props of the element it renders.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | (state) => string | — | A class string, or a function of the part's state. |
| style | CSSProperties | (state) => CSSProperties | — | A style object, or a function of the part's state. |
| render | ReactElement | (props, state) => ReactElement | — | Replace the default element or compose with your own component. Props merge: handlers chain, classes join. |
State
The object passed to className, style and render functions, exported as ClickWheel.State.
| Field | Type | Description |
|---|---|---|
| value | number | The current value. |
| dragging | boolean | A pointer is holding the ring. |
| coasting | boolean | The ring is coasting after release. |
| disabled | boolean | The wheel is disabled. |
Helpers
| Export | Type | Description |
|---|---|---|
| haptic(ms?) | (durationMs?: number) => void | One pulse. The duration applies to the Vibration API, default 4. |
| hapticsSupported() | () => boolean | Whether the browser supports a haptics method used by the component. |
| <HapticTap /> | input props | An invisible switch that covers its parent so a real tap ticks on iOS. The click still bubbles. |
Keyboard
| Key | Action |
|---|---|
| Arrow Up / Arrow Right | + step |
| Arrow Down / Arrow Left | − step |
| Page Up / Page Down | ± one sixth of a turn |
| Home / End | min / max |
The ring announces itself as a slider with aria-valuenow, aria-valuemin and aria-valuemax. Use getAriaValueText for a spoken format.