HextaUI

Input OTP

Slots für Einmalcodes, die Tippen, Einfügen und SMS-Autofill annehmen, mit einer optionalen Animation, die Codes kaskadierend einblendet, und einem Status für die Überprüfung.

Type or paste 123456 to pass. Anything else fails.

"use client"

import * as React from "react"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
  type InputOTPStatus,
} from "@/components/ui/input-otp"
import { Label } from "@/components/ui/label"

export function InputOTPDemo() {
  const [value, setValue] = React.useState("")
  const [status, setStatus] = React.useState<InputOTPStatus>("idle")
  const timer = React.useRef<ReturnType<typeof setTimeout>>(undefined)

  React.useEffect(() => () => clearTimeout(timer.current), [])

  function verify(code: string) {
    setStatus("loading")
    clearTimeout(timer.current)
    timer.current = setTimeout(() => {
      if (code === "123456") {
        setStatus("success")
        return
      }
      setStatus("error")
      timer.current = setTimeout(() => {
        setValue("")
        setStatus("idle")
      }, 900)
    }, 1200)
  }

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-3">
      <Label htmlFor="input-otp-demo">Verification code</Label>
      <InputOTP
        id="input-otp-demo"
        length={6}
        variant="separate"
        animated
        status={status}
        value={value}
        onValueChange={(next) => {
          setValue(next)
          setStatus("idle")
        }}
        onValueComplete={verify}
        aria-describedby="input-otp-demo-hint"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <p id="input-otp-demo-hint" className="text-sm text-muted-foreground">
        Type or paste 123456 to pass. Anything else fails.
      </p>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/input-otp.json

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
<label htmlFor="code">Verification code</label>
<InputOTP id="code" length={6}>
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
</InputOTP>

Rendere pro Zeichen ein <InputOTPSlot /> und setze length auf dieselbe Zahl. Slots finden ihre Position selbst, es gibt also keine index-Prop, die synchron gehalten werden muss.

InputOTP
├── InputOTPGroup
│   └── InputOTPSlot
├── InputOTPSeparator
└── InputOTPGroup
    └── InputOTPSlot

Verbunden

Das Standard-Aussehen. Jede <InputOTPGroup /> verbindet ihre Slots zu einem Streifen mit gemeinsamen Kanten.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
import { Label } from "@/components/ui/label"

export function InputOTPBasic() {
  return (
    <div className="flex max-w-full min-w-0 flex-col items-start gap-2">
      <Label htmlFor="input-otp-basic">Verification code</Label>
      <InputOTP id="input-otp-basic" length={6}>
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
    </div>
  )
}

Getrennt

variant="separate" gibt jedem Slot eine eigene abgerundete Box mit Abstand dazwischen.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPSeparate() {
  return (
    <InputOTP length={6} variant="separate" aria-label="Verification code">
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Größen

sm, default und lg entsprechen den Höhen von Input und Button. Auf Touchscreens wächst jede Größe auf mindestens 44 px mit einer 16-px-Schrift.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPSizes() {
  return (
    <div className="flex max-w-full min-w-0 flex-col items-start gap-4">
      <InputOTP length={4} size="sm" aria-label="Small code">
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <InputOTP length={4} aria-label="Default code">
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <InputOTP length={4} size="lg" aria-label="Large code">
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
    </div>
  )
}

Animiert

animated ist standardmäßig aus. Damit steigen getippte Zeichen ein, gelöschte sinken heraus, während die übrigen nachrücken, und ein ganzer Code aus Autofill, Einfügen oder deinem eigenen State kaskadiert Slot für Slot herein. Drücke Fill code, um die Kaskade zu sehen.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPAnimated() {
  const [value, setValue] = React.useState("")

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-4">
      <InputOTP
        length={6}
        variant="separate"
        animated
        value={value}
        onValueChange={setValue}
        aria-label="Verification code"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <div className="flex gap-2">
        <Button variant="outline" size="sm" onClick={() => setValue("482913")}>
          Fill code
        </Button>
        <Button variant="ghost" size="sm" onClick={() => setValue("")}>
          Clear
        </Button>
      </div>
    </div>
  )
}

Status

status zeigt das Ergebnis der Codeprüfung. loading sperrt die Slots und markiert das Feld als beschäftigt, error markiert jeden Slot als ungültig, und success färbt die Kanten grün. Jeder wird angesagt. Mit animated läuft bei loading eine Welle, bei error wackelt es einmal, und bei success poppen die Zeichen.

"use client"

import * as React from "react"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
  type InputOTPStatus,
} from "@/components/ui/input-otp"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

const statuses: InputOTPStatus[] = ["idle", "loading", "success", "error"]

export function InputOTPStatusExample() {
  const [status, setStatus] = React.useState<InputOTPStatus>("loading")

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-4">
      <InputOTP
        length={6}
        variant="separate"
        animated
        status={status}
        defaultValue="381904"
        aria-label="Verification code"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <ToggleGroup
        aria-label="Status"
        size="sm"
        value={[status]}
        onValueChange={(next) => {
          if (next[0]) {
            setStatus(next[0] as InputOTPStatus)
          }
        }}
      >
        {statuses.map((item) => (
          <ToggleGroupItem key={item} value={item}>
            {item}
          </ToggleGroupItem>
        ))}
      </ToggleGroup>
    </div>
  )
}

Kontrolliert

Übergib value und onValueChange. Der Wert ist immer der gefilterte Code, nie länger als length.

"use client"

import * as React from "react"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPControlled() {
  const [value, setValue] = React.useState("")

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-3">
      <InputOTP
        length={6}
        value={value}
        onValueChange={setValue}
        aria-label="Verification code"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <p className="text-sm text-muted-foreground tabular-nums">
        {value === "" ? "Enter your code." : `You entered: ${value}`}
      </p>
    </div>
  )
}

Form

Mit einem name wird der Code mit dem Formular gesendet. autoSubmit sendet ab, sobald der letzte Slot gefüllt ist, sodass ein automatisch ausgefüllter Code Nutzer ohne weiteres Tippen anmeldet.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"
import { Label } from "@/components/ui/label"

export function InputOTPForm() {
  const [submitted, setSubmitted] = React.useState<string>()

  return (
    <form
      className="flex max-w-full min-w-0 flex-col items-start gap-3"
      onSubmit={(event) => {
        event.preventDefault()
        setSubmitted(String(new FormData(event.currentTarget).get("code")))
      }}
    >
      <Label htmlFor="input-otp-form">Sign-in code</Label>
      <InputOTP id="input-otp-form" name="code" length={6} required autoSubmit>
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <div className="flex items-center gap-3">
        <Button type="submit" size="sm">
          Continue
        </Button>
        <p role="status" className="text-sm text-muted-foreground tabular-nums">
          {submitted ? `Submitted ${submitted}` : null}
        </p>
      </div>
    </form>
  )
}

Mit Field

In einem <Field /> sind Label, Beschreibung und Fehler automatisch verknüpft. Gib etwas anderes als 000000 ein, um den Fehler zu sehen.

"use client"

import * as React from "react"

import {
  Field,
  FieldDescription,
  FieldError,
  FieldLabel,
} from "@/components/ui/field"
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPField() {
  const [value, setValue] = React.useState("")
  const [error, setError] = React.useState<string>()

  return (
    <Field invalid={error !== undefined} className="w-fit">
      <FieldLabel>Verification code</FieldLabel>
      <InputOTP
        length={6}
        value={value}
        onValueChange={(next) => {
          setValue(next)
          setError(undefined)
        }}
        onValueComplete={(code) => {
          if (code !== "000000") {
            setError("That code has expired. Request a new one.")
          }
        }}
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <FieldDescription>We sent it to [email protected].</FieldDescription>
      <FieldError errors={error ? [{ message: error }] : []} />
    </Field>
  )
}

Ungültig

aria-invalid an der Root markiert jeden Slot. Verknüpfe die Meldung mit aria-describedby.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPInvalid() {
  return (
    <div className="flex max-w-full min-w-0 flex-col items-start gap-2">
      <InputOTP
        length={6}
        defaultValue="111111"
        aria-invalid
        aria-label="Verification code"
        aria-describedby="input-otp-invalid-error"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <p id="input-otp-invalid-error" className="text-sm text-destructive">
        That code doesn’t match. Check the latest message.
      </p>
    </div>
  )
}

Buchstaben und Zahlen

validationType="alphanumeric" akzeptiert Wiederherstellungs- und Einladungscodes, und normalizeValue wandelt sie beim Tippen oder Einfügen in Großbuchstaben um.

"use client"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPAlphanumeric() {
  return (
    <InputOTP
      length={8}
      validationType="alphanumeric"
      normalizeValue={(value) => value.toUpperCase()}
      aria-label="Recovery code"
    >
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
      <InputOTPSeparator />
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Maskiert

mask verbirgt jedes Zeichen, für PINs. Schalte Autofill mit autoComplete="off" ab, wenn der Wert kein Einmalcode ist.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPMasked() {
  return (
    <InputOTP
      length={4}
      mask
      animated
      variant="separate"
      autoComplete="off"
      aria-label="PIN"
    >
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Eigenes Trennzeichen

Gruppiere die Slots beliebig und übergib dein eigenes Icon an <InputOTPSeparator />.

import { IconPointFilled } from "@tabler/icons-react"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPCustomSeparator() {
  return (
    <InputOTP length={6} variant="separate" aria-label="Pairing code">
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
      <InputOTPSeparator>
        <IconPointFilled aria-hidden="true" />
      </InputOTPSeparator>
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
      <InputOTPSeparator>
        <IconPointFilled aria-hidden="true" />
      </InputOTPSeparator>
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Deaktiviert

Ein deaktiviertes Feld lässt sich weder fokussieren noch bearbeiten.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPDisabled() {
  return (
    <InputOTP
      length={6}
      defaultValue="12"
      disabled
      aria-label="Verification code"
    >
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Rechts nach links

Slots füllen sich von rechts, und die Pfeiltasten folgen dem, was du siehst. Gib Slots nach dem ersten ein übersetztes aria-label. Setze dir="ltr" am Feld, um einen Code auf einer Rechts-nach-links-Seite von links nach rechts zu halten.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
import { Label } from "@/components/ui/label"

export function InputOTPRtl() {
  return (
    <div
      dir="rtl"
      className="flex max-w-full min-w-0 flex-col items-start gap-2"
    >
      <Label htmlFor="input-otp-rtl">رمز التحقق</Label>
      <InputOTP id="input-otp-rtl" length={6} animated>
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot aria-label="الخانة ٢ من ٦" />
          <InputOTPSlot aria-label="الخانة ٣ من ٦" />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot aria-label="الخانة ٤ من ٦" />
          <InputOTPSlot aria-label="الخانة ٥ من ٦" />
          <InputOTPSlot aria-label="الخانة ٦ من ٦" />
        </InputOTPGroup>
      </InputOTP>
    </div>
  )
}
TasteAktion
TabSetzt den Fokus in das Feld, auf den ersten leeren Slot, und wieder hinaus. Nur ein Slot liegt in der Tab-Reihenfolge.
←→Wechselt zum vorherigen oder nächsten Slot, in Rechts-nach-links-Layouts in visueller Reihenfolge.
Home↑Wechselt zum ersten Slot.
End↓Wechselt zum Slot nach dem letzten Zeichen.
BackspaceLöscht das Zeichen im Slot oder das davor, wenn der Slot leer ist. Spätere Zeichen rücken nach.
DeleteLöscht das Zeichen im Slot und behält den Fokus dort.
CtrlBackspaceLeert den ganzen Code. ⌘ Backspace unter macOS.
CtrlAWählt den ganzen Code aus (⌘ A unter macOS). Backspace oder Entf leert ihn dann und kehrt zum ersten Slot zurück, Tippen oder Einfügen ersetzt ihn, und Strg+C kopiert alles. Jede andere Taste oder ein Klick beendet die Auswahl.
  • Jeder Slot ist ein echtes Input. Der erste übernimmt seinen Namen von deinem <label> oder aria-label; die anderen heißen „Character 2 of 6“ und so weiter. Übergib aria-label an einem Slot, um ihn zu übersetzen.
  • Der erste Slot hat autocomplete="one-time-code", sodass iOS und macOS Codes aus Nachrichten und Mail anbieten, Android SMS-Codes anbietet und Passwortmanager ihn füllen können. Ein ganzer Code, der in einem Slot landet, wird auf alle verteilt. Die animierte Kaskade läuft für jede Quelle, auch für Codes, die du über die WebOTP-API setzt.
  • Wann immer der Code leer wird, während ein Slot den Fokus hat, etwa nachdem ein falscher Code geleert wurde, wandert der Fokus zurück zum ersten Slot, sodass der nächste Versuch an der richtigen Stelle beginnt.
  • Ist status gesetzt, sagt eine verborgene Live-Region neben dem Feld ihn an. Ändere den Wortlaut mit loadingLabel, successLabel und errorLabel.
  • Mit animated werden Zeichen auf einer für Screenreader verborgenen Ebene gezeichnet, während die Inputs den echten Wert behalten. Bei reduzierter Bewegung blenden Zeichen nur ein und aus, und die Statuswelle wird zu einem sanften Pulsieren.

Basiert auf dem Base UI OTP Field. Jede Base-UI-Prop wird durchgereicht.

PropTypStandard
lengthErforderlich. Die Anzahl der Slots; rendere dieselbe Anzahl von InputOTPSlot-Teilen.
number–
variant
"joined" | "separate""joined"
size
"sm" | "default" | "lg""default"
animatedAnimiert Zeichen beim Ein- und Ausblenden, lässt mehrstellige Eingaben kaskadieren und animiert den Status.
booleanfalse
statusDas Ergebnis der Codeprüfung. Loading macht die Slots schreibgeschützt.
"idle" | "loading" | "success" | "error"–
loadingLabel
string"Verifying code"
successLabel
string"Code verified"
errorLabel
string"Code is incorrect"
value
string–
defaultValue
string–
onValueChange
(value: string, details) => void–
onValueCompleteWird aufgerufen, wenn der letzte Slot gefüllt wird.
(value: string, details) => void–
onValueInvalidWird aufgerufen, wenn getippte oder eingefügte Zeichen abgelehnt werden.
(value: string, details) => void–
validationType
"numeric" | "alpha" | "alphanumeric" | "none""numeric"
normalizeValueLäuft nach dem Filtern. Halte es idempotent.
(value: string) => string–
inputModeStandardmäßig aus validationType.
string–
autoComplete
string"one-time-code"
autoSubmit
booleanfalse
mask
booleanfalse
aria-invalidMarkiert jeden Slot als ungültig.
boolean–
name
string–
form
string–
idSitzt am ersten Slot, sodass das htmlFor eines Labels darauf zeigt.
string–
disabled
booleanfalse
readOnly
booleanfalse
required
booleanfalse
className
string | (state) => string–
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="input-otp"Das Root.
data-variant="joined" | "separate"Die aktuelle Variante.
data-sizeDie aktuelle Größe.
data-statusDer Status, wenn einer gesetzt ist.
data-animatedVorhanden, wenn animated aktiv ist.
data-shakeVorhanden, während das Feld wackelt, nachdem der Status auf error gewechselt hat.
data-completeVorhanden, wenn jeder Slot gefüllt ist.
data-filledVorhanden, wenn ein Slot gefüllt ist.
data-focusedVorhanden, solange ein Slot den Fokus hat.
data-disabledVorhanden, wenn deaktiviert.
data-readonlyVorhanden, wenn schreibgeschützt, auch während des Ladens.
data-requiredVorhanden, wenn erforderlich.
data-invalid / data-valid / data-touched / data-dirtyFeldzustand, innerhalb eines Field.
data-slot="input-otp-status"Die verborgene Live-Region, ein Geschwisterelement der Root.

Ein einfaches Element, das eine Folge von Slots anordnet. In der Variante joined teilen sich seine Slots die Kanten.

PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="input-otp-group"Die Gruppe in CSS ansprechen.

Eine Box mit einem Input. className geht an die Box; jede andere Prop geht an das Input.

PropTypStandard
aria-labelWird am ersten Slot ignoriert, der das Label verwendet.
string"Character N of M"
classNameDer State enthält Index, Wert und filled des Slots sowie den Feldzustand.
string | (state) => string–
placeholder
string–
AttributBeschreibung
data-slot="input-otp-slot"Die Box.
data-filledVorhanden, wenn der Slot ein Zeichen hat.
data-statusDer Status der Root, wenn nicht idle.
--input-otp-indexDie Position des Slots, mit der die Statusbewegung gestaffelt wird.
data-slot="input-otp-input"Das Input darin, mit den Base-UI-Attributen data-filled, data-focused, data-complete und den Feld-Attributen.
data-slot="input-otp-char"Das gezeichnete Zeichen, wenn animated aktiv ist.

Ein Trennzeichen mit einem Minus-Icon. Übergib Kinder, um ein anderes Icon zu verwenden.

PropTypStandard
orientation
"horizontal" | "vertical""horizontal"
className
string | (state) => string–
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="input-otp-separator"Das Trennzeichen in CSS ansprechen.

Die Klassennamen hinter einem Slot und einer Gruppe (inputOTPGroupVariants). Rufe sie mit { variant, size } auf.

In Blocks verwendet

Blocks, die auf Input OTP aufbauen.