Chuyển tới nội dung
Component

Select

Picks one value from a list. The trigger looks like Input, in the same four sizes; the list opens over it on the raised surface, with the current value aligned to the trigger and marked with a check.

Xem trước

Props

  • size
  • disabled
  • placeholder
  • defaultValue
  • value
  • open
  • defaultOpen
  • dir
  • label
  • groups
  • aria-invalid
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"

<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="preview-select" className="text-sm text-text-primary">Role</label>
  <Select>
    <SelectTrigger id="preview-select">
      <SelectValue placeholder="Choose a role" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="viewer">Viewer</SelectItem>
      <SelectItem value="editor">Editor</SelectItem>
      <SelectItem value="admin">Admin</SelectItem>
      <SelectItem value="owner">Owner</SelectItem>
    </SelectContent>
  </Select>
</div>

Cách dùng

Khi nào dùng

  • Pick one value from a known list of options, such as the role of a team member.
  • Fit a choice into the space of one field, next to an Input or Button of the same size.
  • A longer list split into labelled groups, with a line between them.

Khi nào không dùng

  • A yes or no choice. Use a native checkbox.
  • Going to another page or running an action. Use a link or Button.
  • Text the user types freely. Use Input.

Biến thể

size

Trigger height from size-element-*, set on SelectTrigger: sm 28px, md 32px (the default), lg 36px, xl 44px with a mouse; on touch md is 36px, lg 44px and xl 48px. Match the Input or Button next to it: md in forms, sm only in dense mouse-first UI, lg or xl for the main field of a screen.

  • sm

  • mdmặc định

  • lg

  • xl

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"

<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="preview-select-size-sm" className="text-sm text-text-primary">Role</label>
  <Select>
    <SelectTrigger id="preview-select-size-sm" size="sm">
      <SelectValue placeholder="Choose a role" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="viewer">Viewer</SelectItem>
      <SelectItem value="editor">Editor</SelectItem>
      <SelectItem value="admin">Admin</SelectItem>
      <SelectItem value="owner">Owner</SelectItem>
    </SelectContent>
  </Select>
</div>
<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="preview-select-size-md" className="text-sm text-text-primary">Role</label>
  <Select>
    <SelectTrigger id="preview-select-size-md">
      <SelectValue placeholder="Choose a role" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="viewer">Viewer</SelectItem>
      <SelectItem value="editor">Editor</SelectItem>
      <SelectItem value="admin">Admin</SelectItem>
      <SelectItem value="owner">Owner</SelectItem>
    </SelectContent>
  </Select>
</div>
<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="preview-select-size-lg" className="text-sm text-text-primary">Role</label>
  <Select>
    <SelectTrigger id="preview-select-size-lg" size="lg">
      <SelectValue placeholder="Choose a role" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="viewer">Viewer</SelectItem>
      <SelectItem value="editor">Editor</SelectItem>
      <SelectItem value="admin">Admin</SelectItem>
      <SelectItem value="owner">Owner</SelectItem>
    </SelectContent>
  </Select>
</div>
<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="preview-select-size-xl" className="text-sm text-text-primary">Role</label>
  <Select>
    <SelectTrigger id="preview-select-size-xl" size="xl">
      <SelectValue placeholder="Choose a role" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="viewer">Viewer</SelectItem>
      <SelectItem value="editor">Editor</SelectItem>
      <SelectItem value="admin">Admin</SelectItem>
      <SelectItem value="owner">Owner</SelectItem>
    </SelectContent>
  </Select>
</div>

Ví dụ

Default

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"

<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="member-role" className="text-sm text-text-primary">Role</label>
  <Select>
    <SelectTrigger id="member-role">
      <SelectValue placeholder="Choose a role" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="viewer">Viewer</SelectItem>
      <SelectItem value="editor">Editor</SelectItem>
      <SelectItem value="admin">Admin</SelectItem>
    </SelectContent>
  </Select>
</div>

Grouped options

Each group is labelled by its heading; the line between groups is decorative.

import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } from "@/components/ui/select"

<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="member-access" className="text-sm text-text-primary">Access</label>
  <Select>
    <SelectTrigger id="member-access">
      <SelectValue placeholder="Choose access" />
    </SelectTrigger>
    <SelectContent>
      <SelectGroup>
        <SelectLabel>Workspace</SelectLabel>
        <SelectItem value="viewer">Viewer</SelectItem>
        <SelectItem value="editor">Editor</SelectItem>
      </SelectGroup>
      <SelectSeparator />
      <SelectGroup>
        <SelectLabel>Billing</SelectLabel>
        <SelectItem value="billing-viewer">Billing viewer</SelectItem>
        <SelectItem value="billing-admin">Billing admin</SelectItem>
      </SelectGroup>
    </SelectContent>
  </Select>
</div>

Disabled

Keeps its value visible but can't be opened.

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"

<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="locked-role" className="text-sm text-text-primary">Role</label>
  <Select defaultValue="owner" disabled>
    <SelectTrigger id="locked-role">
      <SelectValue />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="owner">Owner</SelectItem>
    </SelectContent>
  </Select>
</div>

Invalid

The danger border plus error text linked with aria-describedby.

Choose a role for the new member.

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"

<div className="flex w-full max-w-sm flex-col gap-xs">
  <label htmlFor="invite-role" className="text-sm text-text-primary">Role</label>
  <Select>
    <SelectTrigger id="invite-role" aria-invalid aria-describedby="invite-role-error">
      <SelectValue placeholder="Choose a role" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="viewer">Viewer</SelectItem>
      <SelectItem value="editor">Editor</SelectItem>
    </SelectContent>
  </Select>
  <p id="invite-role-error" className="text-sm text-text-danger">Choose a role for the new member.</p>
</div>

Nên và không nên

Nên

  • Use Select to pick one value from a known list of options.
  • Always pair the trigger with a visible label (htmlFor / id).
  • Keep option labels short and in sentence case, and sort them in a way the user expects (by order of use, or alphabetically).
  • Use a placeholder only to hint at the action (Choose a role), never instead of the label.
  • Match the trigger size to the Input or Button next to it.
  • Split a long list into groups with a SelectLabel heading each (Workspace, Billing), and a SelectSeparator between them.

Không nên

  • Don't use Select for a yes or no choice. Use a checkbox.
  • Don't use Select to navigate or to trigger actions. Use links or Button.
  • Don't put icons, descriptions or other controls inside options; an option is one line of text.
  • Don't use size sm on touch-first screens.
  • Don't restyle colors through className. If a state looks wrong, open an issue instead of overriding it.

Props

TênKiểuGiá trịMặc địnhMô tả
sizeenumsm | md | lg | xlmdTrigger height from size-element-*, set on SelectTrigger: sm 28px, md 32px (the default), lg 36px, xl 44px with a mouse; on touch md is 36px, lg 44px and xl 48px. Match the Input or Button next to it: md in forms, sm only in dense mouse-first UI, lg or xl for the main field of a screen.
disabledboolean-falseSet on Select. The trigger can't be focused or opened and uses the neutral disabled look.
placeholderstring--Set on SelectValue. Shown in the trigger until a value is picked. Not a label.
defaultValuestring--Set on Select. The value picked on first render, when you don't control it.
valuestring--Set on Select. The picked value, when you control it. Pair it with onValueChange.
onValueChangefunction--Set on Select. Called with the new value when the user picks an option.
namestring--Set on Select. Submits the value with a form under this name. Chỉ trên web.
requiredboolean-falseSet on Select. A form can't be submitted until a value is picked. Say it in the label too, not only with this prop. Chỉ trên web.
openboolean--Set on Select. Whether the list is open, when you control it. Pair it with onOpenChange.
defaultOpenboolean-falseSet on Select. Opens the list on first render, when you don't control it.
onOpenChangefunction--Set on Select. Called with true or false when the list opens or closes.
direnumltr | rtl-Set on Select. Reading direction of the list and its keyboard. Leave it out to follow the page. Chỉ trên web.

Slot

TênBắt buộcMô tả
triggercóSelectTrigger with a SelectValue inside.
contentcóSelectContent with one SelectItem per option. Item labels are short nouns in sentence case. Long lists: SelectGroup with a SelectLabel per group, SelectSeparator between groups.

Cấu tạo

PhầnMô tả
rootSelect. Holds the value and the open state; renders nothing itself.
triggerSelectTrigger: the button that shows the value and opens the list. Same fill, border and sizes as Input.
valueSelectValue: the current option's text, or the placeholder when nothing is picked.
iconTabler ChevronDownIcon at the end of the trigger. 16px in sm, md and lg, 20px in xl (DESIGN.md 9.1).
contentSelectContent: the floating list on the raised surface, at least as wide as the trigger. Long lists scroll inside it.
itemSelectItem: one option. Highlighted on hover and with the arrow keys.
indicatorTabler CheckIcon (16px) at the end of the selected option.
optionGroupSelectGroup: options under one heading. Renders no box of its own.
groupLabelSelectLabel: the heading of a group, smaller than an option, in secondary text. Not selectable.
separatorSelectSeparator: a line across the full list between two groups.
scrollButtonSelectContent shows a caret at the top or bottom while more options are out of view; pointing at it scrolls the list. Tabler ChevronUpIcon or CaretDownIcon, 16px.
hitAreaTransparent ::before centered on the trigger, never smaller than size-target-min (24px on mouse, 44px on touch), so sm stays a 44px touch target.

Khả năng truy cập

Role: combobox

PhímHành động
TabMoves focus to the trigger and shows the focus ring.
Enter, Space, ArrowDown, ArrowUpOpens the list with the current value highlighted.
ArrowDown, ArrowUpMoves the highlight while the list is open.
Home, EndHighlights the first or last option.
Enter, SpacePicks the highlighted option, closes the list and returns focus to the trigger.
EscapeCloses the list without changing the value and returns focus to the trigger.
A to ZHighlights the next option that starts with the typed letters.
  • Give the trigger a visible label: an id on SelectTrigger and a label with htmlFor. aria-label only where the label is shown another way, such as a table column header.
  • Invalid: aria-invalid on SelectTrigger plus error text linked with aria-describedby. Never color alone.
  • Disabled uses the native disabled attribute on the trigger, so it leaves the tab order.
  • The trigger is a button with role combobox and the list a listbox; options are announced with their selected state.
  • Single value only. For several values use a group of checkboxes.
  • SelectGroup has role group and is labelled by its SelectLabel, so a screen reader announces the heading when the highlight enters the group. SelectLabel is not focusable and the arrow keys skip it.
  • SelectSeparator is decorative (aria-hidden).

Cài đặt

npx shadcn add https://werewolf-ui.vercel.app/r/select.json

Lệnh này thêm luôn styles/werewolf.css, bộ token mà mọi component dùng, và các component khác mà nó cần. Import file đó một lần trong CSS gốc, ngay sau @import "tailwindcss", với đường dẫn tính từ file CSS đó (từ app/globals.css là @import "../styles/werewolf.css").