OTP Input
A component for entering one-time passwords with individual character slots.

Installation
npx shadcn@latest add @tetra-ui/otp-inputUsage
The API mirrors shadcn Input OTP — InputOTP maps to OTPInput, InputOTPGroup to OTPInputGroup, and so on.
import {
OTPInput,
OTPInputGroup,
OTPInputSeparator,
OTPInputSlot,
} from "@/components/ui/otp-input";<OTPInput maxLength={6} onComplete={(code) => console.log(code)}>
<OTPInputGroup>
<OTPInputSlot index={0} />
<OTPInputSlot index={1} />
<OTPInputSlot index={2} />
</OTPInputGroup>
<OTPInputSeparator />
<OTPInputGroup>
<OTPInputSlot index={3} />
<OTPInputSlot index={4} />
<OTPInputSlot index={5} />
</OTPInputGroup>
</OTPInput>Four digits
A common pattern for PIN codes:
<OTPInput maxLength={4} secureTextEntry onComplete={(code) => console.log(code)}>
<OTPInputSlot index={0} />
<OTPInputSlot index={1} />
<OTPInputSlot index={2} />
<OTPInputSlot index={3} />
</OTPInput>Controlled
const [value, setValue] = useState("");
<OTPInput maxLength={6} onChange={setValue} value={value}>
<OTPInputGroup>
<OTPInputSlot index={0} />
<OTPInputSlot index={1} />
<OTPInputSlot index={2} />
</OTPInputGroup>
<OTPInputSeparator />
<OTPInputGroup>
<OTPInputSlot index={3} />
<OTPInputSlot index={4} />
<OTPInputSlot index={5} />
</OTPInputGroup>
</OTPInput>Disabled
<OTPInput disabled maxLength={6}>
<OTPInputGroup>
<OTPInputSlot index={0} />
<OTPInputSlot index={1} />
<OTPInputSlot index={2} />
</OTPInputGroup>
<OTPInputSeparator />
<OTPInputGroup>
<OTPInputSlot index={3} />
<OTPInputSlot index={4} />
<OTPInputSlot index={5} />
</OTPInputGroup>
</OTPInput>Invalid
<OTPInput invalid maxLength={6}>
<OTPInputGroup>
<OTPInputSlot index={0} />
<OTPInputSlot index={1} />
<OTPInputSlot index={2} />
</OTPInputGroup>
<OTPInputSeparator />
<OTPInputGroup>
<OTPInputSlot index={3} />
<OTPInputSlot index={4} />
<OTPInputSlot index={5} />
</OTPInputGroup>
</OTPInput>Pattern
Restrict input to specific character patterns:
import { OTPInput, REGEXP_ONLY_DIGITS } from "@/components/ui/otp-input";
<OTPInput maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
<OTPInputGroup>
<OTPInputSlot index={0} />
<OTPInputSlot index={1} />
<OTPInputSlot index={2} />
</OTPInputGroup>
<OTPInputSeparator />
<OTPInputGroup>
<OTPInputSlot index={3} />
<OTPInputSlot index={4} />
<OTPInputSlot index={5} />
</OTPInputGroup>
</OTPInput>Form
Use with Field and FieldControl for labels and validation:
import {
Field,
FieldControl,
FieldErrorMessage,
FieldLabel,
} from "@/components/ui/form";
import {
OTPInput,
OTPInputGroup,
OTPInputSeparator,
OTPInputSlot,
} from "@/components/ui/otp-input";
<Field errorMessage={error} invalid={!!error}>
<FieldLabel>Verification code</FieldLabel>
<FieldControl>
<OTPInput maxLength={6} onChange={setValue} value={value}>
<OTPInputGroup>
<OTPInputSlot index={0} />
<OTPInputSlot index={1} />
<OTPInputSlot index={2} />
</OTPInputGroup>
<OTPInputSeparator />
<OTPInputGroup>
<OTPInputSlot index={3} />
<OTPInputSlot index={4} />
<OTPInputSlot index={5} />
</OTPInputGroup>
</OTPInput>
</FieldControl>
<FieldErrorMessage />
</Field>