Slider
Slider
사용자가 주어진 범위에서 값을 선택하는 입력 컴포넌트예요.
출처: 문서
본문
트랙과 썸을 드래그해 범위 내 값을 선택하는 슬라이더 컴포넌트예요. 여러 개의 썸(범위 슬라이더)을 지원하고, 키보드로 완전히 조작할 수 있으며 RTL 방향도 지원해요.
Features
- 제어(controlled)/비제어(uncontrolled) 방식 모두 지원.
- 여러 개의 썸 지원.
- 썸 사이 최소 값 지원.
- 트랙 터치/클릭으로 값 업데이트 지원.
- 오른쪽에서 왼쪽(RTL) 방향 지원.
- 완전한 키보드 내비게이션.
Anatomy
모든 파트를 임포트해 조립해요.
import { Slider } from "radix-ui";
export default () => (
<Slider.Root>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
</Slider.Root>
);
API Reference
Root
슬라이더의 모든 파트를 담아요. form 안에서 사용하면 이벤트 전파가 올바르게 되도록 썸마다 input이 렌더링돼요.
| Prop | Type | Default |
|---|---|---|
asChild |
boolean |
false |
defaultValue |
number[] |
No default value |
value |
number[] |
No default value |
onValueChange |
function |
No default value |
onValueCommit |
function |
No default value |
name |
string |
No default value |
disabled |
boolean |
false |
orientation |
enum |
"horizontal" |
dir |
enum |
No default value |
inverted |
boolean |
false |
min |
number |
0 |
max |
number |
100 |
step |
number |
1 |
minStepsBetweenThumbs |
number |
0 |
form |
string |
No default value |
| Data attribute | Values |
|---|---|
[data-disabled] |
Present when disabled |
[data-orientation] |
"vertical" | "horizontal" |
Track
Slider.Range를 담는 트랙이에요.
| Prop | Type | Default |
|---|---|---|
asChild |
boolean |
false |
| Data attribute | Values |
|---|---|
[data-disabled] |
Present when disabled |
[data-orientation] |
"vertical" | "horizontal" |
Range
범위(range) 파트예요. Slider.Track 안에 있어야 해요.
| Prop | Type | Default |
|---|---|---|
asChild |
boolean |
false |
| Data attribute | Values |
|---|---|
[data-disabled] |
Present when disabled |
[data-orientation] |
"vertical" | "horizontal" |
Thumb
드래그 가능한 썸이에요. 여러 썸을 렌더링할 수 있어요.
| Prop | Type | Default |
|---|---|---|
asChild |
boolean |
false |
| Data attribute | Values |
|---|---|
[data-disabled] |
Present when disabled |
[data-orientation] |
"vertical" | "horizontal" |
Examples
세로 방향
orientation prop으로 세로 슬라이더를 만들 수 있어요.
// index.jsx
import { Slider } from "radix-ui";
import "./styles.css";
export default () => (
<Slider.Root className="SliderRoot" defaultValue={[50]} orientation="vertical">
<Slider.Track className="SliderTrack">
<Slider.Range className="SliderRange" />
</Slider.Track>
<Slider.Thumb className="SliderThumb" />
</Slider.Root>
);
/* styles.css */
.SliderRoot {
position: relative;
display: flex;
align-items: center;
}
.SliderRoot[data-orientation="vertical"] {
flex-direction: column;
width: 20px;
height: 100px;
}
.SliderTrack {
position: relative;
flex-grow: 1;
background-color: grey;
}
.SliderTrack[data-orientation="vertical"] {
width: 3px;
}
.SliderRange {
position: absolute;
background-color: black;
}
.SliderRange[data-orientation="vertical"] {
width: 100%;
}
.SliderThumb {
display: block;
width: 20px;
height: 20px;
background-color: black;
}
범위 만들기
여러 썸과 값을 추가해 범위 슬라이더를 만들어요.
import { Slider } from "radix-ui";
export default () => (
<Slider.Root defaultValue={[25, 75]}>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
<Slider.Thumb />
</Slider.Root>
);
스텝 크기 정의
step prop으로 증가 간격을 늘려요.
import { Slider } from "radix-ui";
export default () => (
<Slider.Root defaultValue={[50]} step={10}>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
</Slider.Root>
);
썸 겹침 방지
minStepsBetweenThumbs로 같은 값을 가진 썸을 피해요.
import { Slider } from "radix-ui";
export default () => (
<Slider.Root defaultValue={[25, 75]} step={10} minStepsBetweenThumbs={1}>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
<Slider.Thumb />
</Slider.Root>
);
숨겨진 input 분리하기
기본적으로 Slider.Thumb는 폼 제출을 위해 시각적으로 숨겨진 input을 렌더링해요. 그 input을 재구성하거나 이동하거나 제외하려면, 더 낮은 레벨의 파트들로 각 썸을 조립할 수 있어요.
중요: 이 파트들은 불안정하며
unstable_접두사가 붙어 있어서 API가 향후 릴리스에서 바뀔 수 있어요.
Slider.unstable_ThumbProvider는 썸 상태를 제공하고 제출 값의 선택적name을 받아요.Slider.unstable_ThumbTrigger는 드래그 가능한 썸 요소예요.Slider.unstable_BubbleInput은Slider.Thumb이 기본으로 렌더링하는 시각적으로 숨겨진 input이에요. 폼 제출이 필요 없으면 생략해요.
import { Slider } from "radix-ui";
export default () => (
<Slider.Root defaultValue={[25, 75]}>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.unstable_ThumbProvider name="price[min]">
<Slider.unstable_ThumbTrigger />
<Slider.unstable_BubbleInput />
</Slider.unstable_ThumbProvider>
<Slider.unstable_ThumbProvider name="price[max]">
<Slider.unstable_ThumbTrigger />
<Slider.unstable_BubbleInput />
</Slider.unstable_ThumbProvider>
</Slider.Root>
);
Accessibility
Slider WAI-ARIA 디자인 패턴을 준수해요.
키보드 상호작용
| Key | Description |
|---|---|
ArrowRight |
Increments/decrements by the step value depending on orientation. |
ArrowLeft |
Increments/decrements by the step value depending on orientation. |
ArrowUp |
Increases the value by the step amount. |
ArrowDown |
Decreases the value by the step amount. |
PageUp |
Increases the value by a larger step. |
PageDown |
Decreases the value by a larger step. |
Shift + ArrowUp |
Increases the value by a larger step. |
Shift + ArrowDown |
Decreases the value by a larger step. |
Home |
Sets the value to its minimum. |
End |
Sets the value to its maximum. |
Custom APIs
primitive 파트를 자신의 컴포넌트로 추상화해 나만의 API를 만들 수 있어요.
모든 파트 추상화
이 예제는 Slider의 모든 파트를 추상화해서 자동으로 닫히는 요소처럼 사용할 수 있게 해요.
사용법
import { Slider } from "./your-slider";
export default () => <Slider defaultValue={[25]} />;
구현
// your-slider.jsx
import { Slider as SliderPrimitive } from "radix-ui";
export const Slider = React.forwardRef((props, forwardedRef) => {
const value = props.value || props.defaultValue;
return (
<SliderPrimitive.Slider {...props} ref={forwardedRef}>
<SliderPrimitive.Track>
<SliderPrimitive.Range />
</SliderPrimitive.Track>
{value.map((_, i) => (
<SliderPrimitive.Thumb key={i} />
))}
</SliderPrimitive.Slider>
);
});
Caveats
마우스 이벤트가 발생하지 않아요
구현 중 마주친 제약 때문에 다음 예시는 기대대로 동작하지 않고 onMouseDown/onMouseUp 이벤트 핸들러가 발생하지 않아요:
<Slider.Root
onMouseDown={() => console.log("onMouseDown")}
onMouseUp={() => console.log("onMouseUp")}
>
…
</Slider.Root>
포인터 이벤트를 사용할 것을 권장해요(예: onPointerDown, onPointerUp). 위 제약과 무관하게, 이 이벤트들은 모든 포인터 입력 타입(마우스·터치·펜 등)에서 발생하므로 크로스 플랫폼/디바이스 처리에 더 적합해요.
더 알아보기 (Learn more)
- 여러
Slider.Thumb와 값을 넣으면 범위(range) 슬라이더가 돼요. Slider.Thumb의 마우스 이벤트는 동작하지 않으므로 포인터 이벤트(onPointerDown등)를 사용해야 해요.