To get started with TanStack Ranger in a React application, use the useRanger hook from @tanstack/react-ranger. You need to provide a reference to a DOM element via getRangerElement and configure the range parameters such as min, max, values, and stepSize.
To render the handles, call rangerInstance.handles(), which returns an array of handle objects containing the necessary event handlers (onKeyDownHandler, onMouseDownHandler, onTouchStart) and state (value, isActive). You can use rangerInstance.getPercentageForValue(value) to calculate the correct CSS position for each handle.
Key configuration options for useRanger:
getRangerElement: A function returning the DOM element that acts as the range container.values: The current array of numeric values for the handles.min: The minimum possible value.max: The maximum possible value.stepSize: The increment size for handle movement.onChange: A callback function triggered when the range changes, receiving the Ranger instance. Use instance.sortedValues to retrieve the updated values.
import React from 'react'
import ReactDOM from 'react-dom'
import { useRanger, Ranger } from '@tanstack/react-ranger'
function App() {
const rangerRef = React.useRef<HTMLDivElement>(null)
const [values, setValues] = React.useState<ReadonlyArray<number>>([
10, 15, 50,
])
const rangerInstance = useRanger<HTMLDivElement>({
getRangerElement: () => rangerRef.current,
values,
min: 0,
max: 100,
stepSize: 5,
onChange: (instance: Ranger<HTMLDivElement>) =>
setValues(instance.sortedValues),
})
return (
<div className="App" style={{ padding: 10 }}>
<h1>Basic Range</h1>
<span>Active Index: {rangerInstance.activeHandleIndex}</span>
<br />
<br />
<div
ref={rangerRef}
style={{
position: 'relative',
userSelect: 'none',
height: '4px',
background: '#ddd',
boxShadow: 'inset 0 1px 2px rgba(0,0,0,.6)',
borderRadius: '2px',
}}
>
{rangerInstance
.handles()
.map(
(
{
value,
onKeyDownHandler,
onMouseDownHandler,
onTouchStart,
isActive,
},
i,
) => (
<button
key={i}
onKeyDown={onKeyDownHandler}
onMouseDown={onMouseDownHandler}
onTouchStart={onTouchStart}
role="slider"
aria-valuemin={rangerInstance.options.min}
aria-valuemax={rangerInstance.options.max}
aria-valuenow={value}
style={{
position: 'absolute',
top: '50%',
left: `${rangerInstance.getPercentageForValue(value)}%`,
zIndex: isActive ? '1' : '0',
transform: 'translate(-50%, -50%)',
width: '14px',
height: '14px',
outline: 'none',
borderRadius: '100%',
background: 'linear-gradient(to bottom, #eee 45%, #ddd 55%)',
border: 'solid 1px #888',
}}
/>
),
)}
</div>
<br />
<br />
<br />
<pre
style={{
display: 'inline-block',
textAlign: 'left',
}}
>
<code>
{JSON.stringify({
values,
})}
</code>
</pre>
</div>
)
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
)