Select
Select
버튼으로 트리거되는, 사용자가 고를 수 있는 옵션 목록을 표시하는 컴포넌트예요.
출처: 문서
본문
버튼을 누르면 선택지를 나열한 메뉴가 펼쳐지고, 그중 하나를 골라 값을 설정하는 드롭다운 셀렉트예요.
<Select.Root defaultValue="apple">
<Select.Trigger />
<Select.Content>
<Select.Group>
<Select.Label>Fruits</Select.Label>
<Select.Item value="orange">Orange</Select.Item>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="grape" disabled>
Grape
</Select.Item>
</Select.Group>
<Select.Separator />
<Select.Group>
<Select.Label>Vegetables</Select.Label>
<Select.Item value="carrot">Carrot</Select.Item>
<Select.Item value="potato">Potato</Select.Item>
</Select.Group>
</Select.Content>
</Select.Root>
Root
Select의 모든 부분을 담아요. Select primitive의 Root 파트에서 props를 상속받아요.
| Prop | Type | Default |
|---|---|---|
size |
Responsive<"1" | "2" | "3"> |
"2" |
Trigger
Select를 열고 닫는 버튼이에요. 이 컴포넌트는 Select primitive의 Trigger와 Value 파트에서 props를 상속받아요. 공통 margin props도 지원해요.
| Prop | Type | Default |
|---|---|---|
variant |
"classic" | "surface" | "soft" | "ghost" |
"surface" |
color |
enum |
No default value |
radius |
"none" | "small" | "medium" | "large" | "full" |
No default value |
placeholder |
string |
No default value |
Content
Select가 열렸을 때 튀어나오는 컴포넌트예요. Select.Portal primitive와 Select.Content primitive 파트에서 props를 상속받아요.
| Prop | Type | Default |
|---|---|---|
variant |
"solid" | "soft" |
"solid" |
color |
enum |
No default value |
highContrast |
boolean |
No default value |
Item
Select의 항목을 담는 컴포넌트예요. Select.Item primitive 파트에서 props를 상속받아요.
Group
여러 항목을 묶는 데 사용해요. Select.Group primitive 파트에서 props를 상속받아요. Select.Label과 함께 사용하면 자동 라벨링으로 좋은 접근성을 보장할 수 있어요.
Label
그룹의 라벨을 렌더링하는 데 사용하며, 화살표 키로 포커스되지 않아요. Select.Label primitive 파트에서 props를 상속받아요.
Separator
Select 안의 항목을 시각적으로 구분하는 데 사용해요. Select.Separator primitive 파트에서 props를 상속받아요.
예시 (Examples)
Size
size prop으로 크기를 제어할 수 있어요.
<Flex gap="3" align="center">
<Select.Root size="1" defaultValue="apple">
<Select.Trigger />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root size="2" defaultValue="apple">
<Select.Trigger />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root size="3" defaultValue="apple">
<Select.Trigger />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
</Flex>
Variant
Trigger와 Content의 variant prop으로 시각적 스타일을 커스터마이징할 수 있어요.
<Flex gap="3" align="center">
<Select.Root defaultValue="apple">
<Select.Trigger variant="surface" />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger variant="classic" />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger variant="soft" />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
</Flex>
Ghost
ghost 트리거 variant를 사용하면 시각적으로 감싸는 요소 없이 트리거를 렌더링할 수 있어요. Ghost 트리거는 레이아웃에서 다르게 동작하는데, 옆 요소들과 시각적으로 정렬되도록 음수 마진을 사용하면서도 패딩이 있는 활성/호버 상태는 유지해요.
<Flex gap="3" align="center">
<Select.Root defaultValue="apple">
<Select.Trigger variant="surface" />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger variant="ghost" />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
</Flex>
Color
Trigger와 Content의 color prop으로 특정 색상 값을 지정할 수 있어요.
<Flex gap="3">
<Select.Root defaultValue="apple">
<Select.Trigger color="indigo" variant="soft" />
<Select.Content color="indigo">
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger color="cyan" variant="soft" />
<Select.Content color="cyan">
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger color="orange" variant="soft" />
<Select.Content color="orange">
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger color="crimson" variant="soft" />
<Select.Content color="crimson">
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
</Flex>
High-contrast
Content의 highContrast prop으로 항목 대비를 높일 수 있어요.
<Flex gap="3">
<Select.Root defaultValue="apple">
<Select.Trigger color="gray" />
<Select.Content color="gray" variant="solid">
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger color="gray" />
<Select.Content color="gray" variant="solid" highContrast>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
</Flex>
Radius
radius prop으로 특정 둥근 모서리 값을 지정할 수 있어요.
<Flex gap="3">
<Select.Root defaultValue="apple">
<Select.Trigger radius="none" />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger radius="large" />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
<Select.Root defaultValue="apple">
<Select.Trigger radius="full" />
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
</Flex>
Placeholder
placeholder prop으로 초기 값이 필요 없는 Trigger를 만들 수 있어요.
<Select.Root>
<Select.Trigger placeholder="Pick a fruit" />
<Select.Content>
<Select.Group>
<Select.Label>Fruits</Select.Label>
<Select.Item value="orange">Orange</Select.Item>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="grape" disabled>
Grape
</Select.Item>
</Select.Group>
<Select.Separator />
<Select.Group>
<Select.Label>Vegetables</Select.Label>
<Select.Item value="carrot">Carrot</Select.Item>
<Select.Item value="potato">Potato</Select.Item>
</Select.Group>
</Select.Content>
</Select.Root>
Position
position="popper" prop을 설정하면 셀렉트 메뉴를 트리거 아래에 배치할 수 있어요.
<Select.Root defaultValue="apple">
<Select.Trigger />
<Select.Content position="popper">
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
With SSR
서버 사이드 렌더링을 사용할 때, 하이드레이션 후 레이아웃이 흔들리는(layout shift) 현상이 생길 수 있어요. Trigger가 선택된 항목의 텍스트를 표시하기 위해 클라이언트 코드를 실행하기 때문이에요. 이 레이아웃 변화를 피하려면 값을 매핑해 직접 렌더링할 수 있어요.
() => {
const data = {
apple: "Apple",
orange: "Orange",
};
const [value, setValue] = React.useState("apple");
return (
<Select.Root value={value} onValueChange={setValue}>
<Select.Trigger>{data[value]}</Select.Trigger>
<Select.Content>
<Select.Item value="apple">Apple</Select.Item>
<Select.Item value="orange">Orange</Select.Item>
</Select.Content>
</Select.Root>
);
};
With an icon
Trigger가 값을 렌더링하는 방식을 자식 요소를 직접 제어해서 커스터마이징할 수 있어요. 예를 들어 선택된 항목의 텍스트 옆에 아이콘을 렌더링할 수 있어요.
() => {
const data = {
light: { label: "Light", icon: <SunIcon /> },
dark: { label: "Dark", icon: <MoonIcon /> },
};
const [value, setValue] = React.useState("light");
return (
<Flex direction="column" maxWidth="160px">
<Select.Root value={value} onValueChange={setValue}>
<Select.Trigger>
<Flex as="span" align="center" gap="2">
{data[value].icon}
{data[value].label}
</Flex>
</Select.Trigger>
<Select.Content position="popper">
<Select.Item value="light">Light</Select.Item>
<Select.Item value="dark">Dark</Select.Item>
</Select.Content>
</Select.Root>
</Flex>
);
};
더 알아보기 (Learn more)
- SSR 환경에서는 레이아웃 변화를 피하려고 자식 요소를 직접 매핑해 렌더링하는 방법도 있어요.