Przejdź do treści

Input OTP

Stabilny

Segmentowane pole na kody jednorazowe, PIN-y i kody odzyskiwania — z obsługą wklejania, autouzupełniania z SMS-ów i menedżerów haseł.

Instalacja

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

Użycie

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
<InputOTP maxLength={6}>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
    <InputOTPSlot index={2} />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot index={3} />
    <InputOTPSlot index={4} />
    <InputOTPSlot index={5} />
  </InputOTPGroup>
</InputOTP>

Input OTP jest zbudowany na input-otp: sloty to tylko obraz jednego prawdziwego pola, które leży pod spodem. Dlatego działają wklejanie, autouzupełnianie z SMS-ów, menedżery haseł, cofanie zmian i czytniki ekranu — do wypełnienia jest jedno pole, a nie sześć.

Przykłady

Tylko cyfry

Przekaż pattern, żeby odrzucać niepasujące znaki już podczas pisania. REGEXP_ONLY_DIGITS pasuje do PIN-ów i kodów z SMS-ów, a domyślny inputMode="numeric" wyświetla na telefonach klawiaturę numeryczną. Wzorce są reeksportowane z @/components/ui/input-otp; podobnie jak samego komponentu, używaj ich w komponentach klienckich.

Litery i cyfry

W kodach odzyskiwania dopuść litery wzorcem REGEXP_ONLY_DIGITS_AND_CHARS i przełącz inputMode na text, żeby dało się je wpisać z klawiatury telefonu. pasteTransformer czyści wklejane wartości — tutaj usuwa myślniki i zamienia litery na wielkie.

Tryb kontrolowany z automatycznym wysłaniem

Kontroluj wartość przez valueonChange, a weryfikuj ją w onComplete, który wywołuje się, gdy wszystkie sloty są wypełnione. Na czas weryfikacji wyłącz pole, po nieudanej próbie zachowaj to, co ktoś wpisał, i napisz, dokąd wysłano kod.

Stan błędu

Ustaw aria-invalid na InputOTP, a wszystkie sloty zmienią kolor na czerwony — nie trzeba oznaczać ich pojedynczo. Wyjaśnij problem w FieldError powiązanym przez aria-describedby.

4
8
1
2
0
9

Wytyczne

Kiedy używać

  • Kody o stałej długości, które ktoś przepisuje z innego miejsca: kody uwierzytelniania dwuskładnikowego, kody weryfikacyjne z e-maili, PIN-y, kody odzyskiwania.

Kiedy nie używać

  • Hasła i wszystko o zmiennej długości — użyj komponentu Inputtype="password".
  • Kody dłuższe niż mniej więcej 8 znaków — łatwiej wypełnić pojedyncze pole z czytelną wskazówką co do formatu.

Dziel długie kody

Pamięć krótkotrwała mieści naraz trzy, cztery znaki. Podziel kody sześciocyfrowe na dwie grupy po trzy, a ośmioznakowe na dwie grupy po cztery za pomocą InputOTPSeparator — i zachowaj taki sam podział jak w e-mailu lub SMS-ie, który dostarcza kod.

Dobrze.Dwie grupy po trzy odpowiadają temu, jak ludzie czytają i zapamiętują kod.
Źle.Osobne pola na każdą cyfrę psują wklejanie, autouzupełnianie i obsługę czytników ekranu.

Wybaczaj błędy

Wysyłaj kod automatycznie po wypełnieniu pola, ale nigdy nie zostawiaj ludzi bez wyjścia: po nieudanej próbie zachowaj to, co wpisali, pozwól poprawić dowolny slot i zamiast ślepego zaułka zaproponuj Wyślij kod ponownie z widocznym czasem oczekiwania.

Dostępność

  • Jedno pole. Czytniki ekranu ogłaszają jedno pole tekstowe i odczytują całą wartość; w kolejności tabulacji pole zajmuje jedno miejsce, a nie po jednym na każdy slot.
  • Podpisz pole. Nadaj InputOTP id, do którego odwoła się FieldLabel, albo aria-label, np. Kod weryfikacyjny.
  • Autouzupełnianie. autoComplete="one-time-code" jest ustawione domyślnie, więc iOS i Android podpowiadają kody z SMS-ów i e-maili.
  • Menedżery haseł. Przy pushPasswordManagerStrategy="increase-width" (wartość domyślna) pole niewidocznie się poszerza, żeby ikony menedżerów haseł nie zasłaniały ostatniego slotu.
  • Kontrast. Obramowania slotów używają tokenu input o kontraście 3:1; aktywny slot dostaje dodatkowo poświatę fokusu w kolorze marki.
KlawiszDziałanie
Tab
Przenosi fokus do pola. Kursor trafia do pierwszego pustego slotu.
0–9 / A–Z
Wypełnia aktywny slot i przechodzi do następnego. Znaki niepasujące do pattern są ignorowane.
Backspace
Usuwa poprzedni znak.
Przesuwa kursor między slotami.
CtrlVV
Wkleja kod i rozkłada go na sloty.

Dokumentacja API

InputOTP

Renderuje kontener i jedno ukryte pole <input>. Przyjmuje wszystkie natywne propsy pola input oraz opcje biblioteki input-otp.

PropTypDomyślnie
maxLengthwymagany

Liczba znaków — musi odpowiadać liczbie slotów.

numberBrak wartości domyślnej
value

Wartość kontrolowana.

stringBrak wartości domyślnej
onChange

Wywoływana z nową wartością przy każdej zmianie.

(value: string) => voidBrak wartości domyślnej
onComplete

Wywoływana, gdy wszystkie sloty są wypełnione.

(value: string) => voidBrak wartości domyślnej
pattern

Wyrażenie regularne, do którego musi pasować każdy znak. Użyj eksportowanych REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS lub REGEXP_ONLY_DIGITS_AND_CHARS.

stringBrak wartości domyślnej
pasteTransformer

Czyści wklejony tekst przed walidacją, np. usuwa myślniki.

(pasted: string) => stringBrak wartości domyślnej
inputMode

Wirtualna klawiatura, która ma się pojawić. Dla kodów z literami użyj text.

string"numeric"
textAlign

Gdzie ustawia się kursor, gdy pole dostaje fokus po kliknięciu poza slotem.

"left" | "center" | "right""left"
pushPasswordManagerStrategy

Robi miejsce na ikony menedżerów haseł.

"increase-width" | "none""increase-width"
containerClassName

Klasy zewnętrznego kontenera. className stylizuje ukryte pole.

stringBrak wartości domyślnej
disabled

Wyłącza pole i przygasza wszystkie sloty.

booleanfalse

InputOTPGroup

Renderuje <div>, który łączy swoje sloty w jeden obramowany segment.

InputOTPSlot

Renderuje komórkę na jeden znak. Przyjmuje wszystkie propsy div.

PropTypDomyślnie
indexwymagany

Pozycja znaku wyświetlanego w tym slocie, liczona od zera.

numberBrak wartości domyślnej

InputOTPSeparator

Renderuje <div role="separator"> z kreską między grupami.