Przejdź do treści

Select

Stabilny

Pozwala wybrać jedną opcję — albo kilka — z listy, która pozostaje schowana, dopóki nie jest potrzebna.

Anatomia

  1. 1WyzwalaczWygląda jak pole tekstowe i ma te same wysokości, więc w formularzu równa się z pozostałymi polami.
  2. 2WartośćEtykieta wybranej opcji — albo placeholder w przygaszonym kolorze, gdy nic nie jest wybrane.
  3. 3IkonaSzewrony w górę i w dół: lista otwiera się, przykrywając wyzwalacz, a wybrana opcja wypada dokładnie w jego miejscu.

Instalacja

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/select.json

Użycie

import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"
const regions = [
  { label: "Select a region", value: null },
  { label: "Frankfurt", value: "fra1" },
  { label: "Tokyo", value: "hnd1" },
]

<Select items={regions}>
  <SelectTrigger>
    <SelectValue />
  </SelectTrigger>
  <SelectContent>
    <SelectGroup>
      <SelectItem value="fra1">Frankfurt</SelectItem>
      <SelectItem value="hnd1">Tokyo</SelectItem>
    </SelectGroup>
  </SelectContent>
</Select>

Przekaż items do komponentu głównego. Dzięki temu SelectValue wyświetla etykietę wybranej opcji zamiast jej surowej wartości, a element z value: null staje się placeholderem widocznym, dopóki nic nie jest wybrane. Umieszczaj ten element na liście tylko wtedy, gdy wyczyszczenie wyboru to pełnoprawna opcja — i wtedy nazwij go jak opcję, na przykład Brak roli.

Przykłady

Grupy

Długie listy podziel na grupy z etykietami i oddziel je separatorami. Etykiety niech będą krótkimi rzeczownikami — to nagłówki, a nie opcje, więc nie da się ich wybrać.

Rozmiary

Wyzwalacz ma rozmiary sm, defaultlg — te same wysokości 32, 36 i 40 px co InputButton, więc pasek filtrów układa się w równej linii bez ani jednej własnej klasy.

Wyrównanie do opcji i tryb popper

Domyślnie lista otwiera się wyrównana do wyzwalacza: wybrana opcja leży dokładnie na wyzwalaczu, jak w natywnym menu macOS, więc wzrok nie musi nigdzie wędrować. Ustaw alignItemWithTrigger={false} na SelectContent, aby uzyskać klasyczną listę rozwijaną, która otwiera się pod spodem — to lepszy wybór przy dolnej krawędzi okna, w gęstych paskach narzędzi i wszędzie tam, gdzie zasłonięcie wyzwalacza ukryłoby kontekst.

Wielokrotny wybór

Ustaw multiple, a do defaultValue przekaż tablicę. Funkcja przekazana do SelectValue pozwala zdecydować, ile wybranych opcji wymienić z nazwy, zanim zastąpi je podsumowanie — długie listy po przecinku zostają ucięte i przestają być czytelne.

<Select items={channels} multiple defaultValue={["email", "slack"]}>
  <SelectTrigger>
    <SelectValue>
      {(value: string[]) =>
        value.length === 1 ? labelFor(value[0]) : `${value.length} channels`
      }
    </SelectValue>
  </SelectTrigger>

</Select>

Rozbudowane opcje i obiekty jako wartości

Opcja może zawierać więcej niż samą etykietę. Gdy wartości są obiektami, za pomocą itemToStringValue powiedz liście wyboru, jak serializować je na potrzeby formularzy, a wybraną opcję wyrenderuj sam przez funkcję renderującą SelectValue. Klasa h-auto! pozwoli wyzwalaczowi urosnąć.

Z ikonami i wyłączonymi opcjami

Elementy wizualne na początku opcji — kropki statusu, flagi, awatary — ułatwiają szybkie przejrzenie listy. Umieść je zarówno w opcji, jak i w funkcji renderującej wartość, żeby wyzwalacz odzwierciedlał wybór. Wyłączone opcje pozostają widoczne, więc wiadomo, że istnieją, ale nie da się ich podświetlić ani wybrać.

W formularzu

Połącz z Field, aby dodać etykietę, opis i komunikat błędu. htmlForFieldLabel wskazuje id wyzwalacza; namerequired wysyłają i walidują wartość jak natywna kontrolka. Aby pokazać błąd, oznacz wyzwalacz atrybutem aria-invalid, a pole — atrybutem data-invalid.

Wytyczne

Kiedy używać

  • Do wyboru spośród pięciu do piętnastu wzajemnie wykluczających się opcji, których nie trzeba porównywać obok siebie.
  • Gdy brakuje miejsca, a bieżący wybór jest ważniejszy niż pozostałe możliwości.

Kiedy nie używać

  • Przy dwóch do pięciu opcjach, które mieszczą się na ekranie — pokaż je wszystkie za pomocą Radio Group lub Toggle Group; chowanie ich za kliknięciem spowalnia pracę.
  • Przy długich listach, które trzeba przeszukiwać — użyj komponentu Combobox.
  • Do wywoływania akcji, takich jak Duplikuj czy Usuń — użyj komponentu Dropdown Menu. Lista wyboru przechowuje wartość, a menu wykonuje polecenie.
  • Gdy lepiej sprawdzi się natywny selektor platformy, na przykład w formularzach wypełnianych głównie na telefonach — użyj komponentu Native Select.

Kolejność i sformułowania

Układaj opcje tak, jak myślą o nich ludzie: według częstości, według wielkości (Mały, Średni, Duży) albo alfabetycznie — nigdy w kolejności, w jakiej trafiły do bazy danych. Pisz krótkie etykiety o jednakowej budowie, a placeholder niech opisuje czynność (Wybierz region), a nie pole (Region).

Rozmiar instancjiMały · 1 vCPUŚredni · 2 vCPUDuży · 4 vCPU
Dobrze.Opcje uporządkowane według wielkości, z krótkimi etykietami o tej samej budowie.
large-4cpuMała instancja (1 vCPU)ŚREDNI
Źle.Przypadkowa kolejność i niespójne etykiety zmuszają do przeczytania każdej opcji.

Wartości domyślne

Wybierz wartość z góry, gdy istnieje bezpieczna, typowa odpowiedź — większości osób oszczędzisz w ten sposób jednej decyzji. Zostaw listę pustą, gdy zły wybór ma konsekwencje (kraj rozliczeniowy, poziom uprawnień) — wtedy wybór jest świadomy, a walidacja może wychwycić pominięcie.

Dostępność

Wyzwalacz to buttonrole="combobox", który steruje rozwijaną listą listbox. Base UI zarządza fokusem, aria-expanded, podświetlaniem z klawiatury i wyszukiwaniem przez wpisywanie, a wartość wysyła przez ukryte pole formularza.

KlawiszDziałanie
EnterSpacja
Otwiera listę z podświetloną wybraną opcją. Gdy lista jest otwarta, Enter lub Spacja wybiera podświetloną opcję.
Przesuwa podświetlenie między opcjami, pomijając wyłączone.
HomeEnd
Podświetla pierwszą lub ostatnią opcję.
A–Z
Wyszukiwanie przez wpisywanie: przechodzi do następnej opcji, która zaczyna się od wpisanych znaków.
Esc
Zamyka listę bez zmiany wartości i przywraca fokus na wyzwalacz.
Tab
Zamyka listę i przenosi fokus do następnej kontrolki.
  • Nadaj etykietę każdej liście wyboru. Powiąż widoczny FieldLabel z wyzwalaczem przez htmlFor albo nadaj wyzwalaczowi aria-label, gdy kontekst wizualnie jasno pokazuje jego przeznaczenie (na przykład w pasku narzędzi).
  • Krawędzie. Obramowanie wyzwalacza używa tokenu input, który ma kontrast co najmniej 3:1 względem strony, zgodnie z WCAG 1.4.11. Stany zaznaczenia i podświetlenia nigdy nie opierają się wyłącznie na kolorze: wybraną opcję oznacza znacznik wyboru.
  • Rozmiar celu. Opcje mają co najmniej 32 px wysokości, wyzwalacz również — więcej niż minimum 24 × 24 px z WCAG 2.2.
  • Modalność. Otwarta lista jest domyślnie modalna: przewijanie strony jest zablokowane, a kliknięcie poza listą najpierw ją zamyka. Ustaw modal={false} na Select, jeśli strona musi pozostać interaktywna.

Dokumentacja API

Select

Komponent główny. Nie renderuje żadnego elementu. Przyjmuje wszystkie propsy Select.Root z Base UI.

PropTypDomyślnie
items

Etykiety opcji. Pozwalają SelectValue wyświetlić etykietę zamiast surowej wartości; element z wartością null dostarcza placeholder.

Record<string, ReactNode> | { label, value }[] | Group[]Brak wartości domyślnej
value

Wybrana wartość. W połączeniu z onValueChange tworzy kontrolowaną listę wyboru.

Value | Value[] | nullBrak wartości domyślnej
defaultValue

Początkowo wybrana wartość w trybie niekontrolowanym.

Value | Value[] | nullBrak wartości domyślnej
onValueChange

Wywoływana, gdy zmienia się wybór.

(value, details) => voidBrak wartości domyślnej
multiple

Pozwala wybrać kilka opcji; wartość staje się tablicą.

booleanfalse
itemToStringValue

Serializuje wartości obiektowe na potrzeby wysyłki formularza.

(value) => stringBrak wartości domyślnej
itemToStringLabel

Zamienia wartości obiektowe na tekst wyświetlany w wyzwalaczu.

(value) => stringBrak wartości domyślnej
isItemEqualToValue

Własny sposób porównywania wartości obiektowych. Domyślnie Object.is.

(itemValue, value) => booleanBrak wartości domyślnej
name

Wysyła wartość z formularzem pod tą nazwą.

stringBrak wartości domyślnej
required

Wymaga wybrania wartości przed wysłaniem formularza.

booleanfalse
disabled

Wyłącza całą listę wyboru.

booleanfalse
readOnly

Pokazuje wartość, ale nie pozwala jej zmienić.

booleanfalse
open / defaultOpen / onOpenChange

Sterują widocznością rozwijanej listy.

boolean / boolean / (open, details) => voidBrak wartości domyślnej
modal

Gdy lista jest otwarta, blokuje przewijanie strony i interakcję poza nią.

booleantrue

SelectTrigger

Przycisk, który pokazuje wartość i otwiera listę. Renderuje <button>.

PropTypDomyślnie
size

Wysokość i wewnętrzne odstępy: 32, 36 lub 40 px — takie same jak w Input i Button.

"sm" | "default" | "lg""default"
aria-invalid

Pokazuje stan błędu. Połącz z data-invalid na otaczającym Field.

booleanBrak wartości domyślnej

SelectValue

Wyświetla wybór wewnątrz wyzwalacza. Renderuje <span>.

PropTypDomyślnie
children

Własny sposób wyświetlania wybranej wartości, np. podsumowanie przy wielokrotnym wyborze.

ReactNode | (value) => ReactNodeBrak wartości domyślnej
placeholder

Wyświetlany, gdy nic nie jest wybrane. Pierwszeństwo ma element z wartością null w items.

ReactNodeBrak wartości domyślnej

SelectContent

Pozycjonowany popup z przewijaną listą opcji.

PropTypDomyślnie
alignItemWithTrigger

Nakłada listę na wyzwalacz tak, że wybrana opcja leży dokładnie na nim. Ustaw false, aby lista otwierała się obok wyzwalacza jak klasyczna lista rozwijana.

booleantrue
side

Preferowana strona, gdy lista nie jest wyrównana do wyzwalacza.

"top" | "bottom" | "left" | "right""bottom"
sideOffset

Odstęp między wyzwalaczem a listą, w pikselach.

number6
align

Wyrównanie wzdłuż wybranej strony.

"start" | "center" | "end""center"
alignOffset

Przesunięcie wzdłuż osi wyrównania, w pikselach.

number0

SelectItem

Opcja. Renderuje <div>role="option".

PropTypDomyślnie
valuewymagany

Wartość, którą reprezentuje ta opcja.

anyBrak wartości domyślnej
disabled

Uniemożliwia podświetlenie i wybranie opcji.

booleanfalse
label

Tekst używany przy wyszukiwaniu przez wpisywanie, gdy zawartość opcji nie jest zwykłym tekstem.

stringBrak wartości domyślnej

SelectGroup, SelectLabel, SelectSeparator

SelectGroup grupuje opcje (i musi je otaczać), SelectLabel nadaje grupie tytuł, a SelectSeparator rysuje linię podziału między grupami. Przyjmują propsy swoich odpowiedników z Base UI.