OTP Input
A one-time password / verification code input with four or six digit boxes. It accepts a pasted code, moves focus automatically and can offer a Resend action.
Installation
npx sdaia-ui@latest add otp-input
Usage
import { OtpInput } from "@/components/ui/OtpInput"<OtpInput length={6} label="Verification code" onComplete={verify} />Default
Four empty boxes with the helper text and a Resend action.
Loading demo: otp-input-default
Examples
Six digits
Length 6 for six-digit codes. Paste a code to fill every box.
Loading demo: otp-input-six-digits
Filled
Every digit is entered.
Loading demo: otp-input-filled
Error
The code is wrong: the boxes turn red and the message explains what to do.
Loading demo: otp-input-error
Disabled
The boxes and the Resend action cannot be used.
Loading demo: otp-input-disabled
RTL
Labels mirror; the digits stay left to right.
Loading demo: otp-input-rtl
Verify your phone
onComplete checks the code; Resend starts a 30-second cool-down with resendDisabled.
Loading demo: otp-input-verify
Props
Prop | Type | Default | Required |
|---|---|---|---|
length | 4 | 6 | 4 | optional |
value | string | — | optional |
defaultValue | string | "" | optional |
onChange | (value: string) => void | — | optional |
onComplete | (value: string) => void | — | optional |
label | string | — | optional |
helperText | string | — | optional |
error | string | — | optional |
disabled | boolean | false | optional |
resendQuestion | string | — | optional |
onResend | () => void | — | optional |
resendHref | string | — | optional |
resendDisabled | boolean | false | optional |
name | string | — | optional |
autoFocus | boolean | false | optional |
labels | Partial<OtpInputLabels> | — | optional |
dir | "ltr" | "rtl" | — | optional |
Accessibility
- One group (role="group") named by the label; each box is an input named "Digit 1 of 6" (localise with labels.digit).
- inputmode="numeric" opens the number keyboard, and autocomplete="one-time-code" on the first box lets the phone fill the code from the SMS.
- Typing moves to the next box; Backspace clears and moves back; Arrow Left / Right, Home and End move between boxes; pasting fills every box. Letters are ignored.
- The error sets aria-invalid on every box, is linked with aria-describedby and is announced (role="alert").
- The digits always run left to right, also in Arabic, so Arrow Left always means the previous box.
- Resend is a real button (or a link with resendHref) with a 24 px target; resendDisabled disables it during a cool-down.
Responsive behaviour
- Each box is 48 × 56 px; six boxes and their gaps take 328 px, which fits a 360 px phone with 16 px gutters.
- On narrower containers the boxes shrink instead of overflowing; the Resend row wraps.
- The digit size follows the responsive Display xs token.