OTP Field
A one-time code input with a slot per character - the equivalent of shadcn/ui's input-otp.
Installation
npx shadcn@latest add @fujin/otp-fieldUsage
import {
OTPField,
OTPFieldGroup,
OTPFieldSeparator,
OTPFieldSlot,
} from "@/components/ui/otp-field"<Field name="code">
<FieldLabel>Verification code</FieldLabel>
<OTPField length={6} onValueComplete={verify} />
</Field>With no children, OTPField renders length slots in one group. Codes are
digits only by default; the first slot has autoComplete="one-time-code" so
iOS and Android offer the code from an SMS, and pasting the whole code into
any slot fills every slot.
Grouped slots
<OTPField length={6}>
<OTPFieldGroup>
<OTPFieldSlot />
<OTPFieldSlot />
<OTPFieldSlot />
</OTPFieldGroup>
<OTPFieldSeparator />
<OTPFieldGroup>
<OTPFieldSlot />
<OTPFieldSlot />
<OTPFieldSlot />
</OTPFieldGroup>
</OTPField>Verifying the code
onValueComplete fires when every slot is filled, whether typed or pasted.
Keep the entered code on failure and show the error with FieldError - do
not clear the slots.
const [invalid, setInvalid] = React.useState(false)
<Field name="code" invalid={invalid}>
<FieldLabel>Verification code</FieldLabel>
<OTPField
length={6}
onValueChange={() => setInvalid(false)}
onValueComplete={async (code) => setInvalid(!(await verify(code)))}
/>
<FieldError errors={[invalid ? "That code is not valid." : null]} />
</Field>Pass autoSubmit to submit the surrounding form instead.
Alphanumeric codes
<OTPField
length={8}
validationType="alphanumeric"
normalizeValue={(value) => value.toUpperCase()}
/>What's different from shadcn/ui
shadcn's input-otp draws fake slots over one hidden input. Base UI's
OTPField uses a real input per slot:
- Slots have no
indexprop - the order comes from the DOM. - The root takes
length(required) instead ofmaxLength. - Characters are filtered by
validationType("numeric"by default) instead of a regexpattern. - Slots after the first are labelled "Character n of m" automatically; pass
aria-labelon a slot to localise it.
API Reference
Prop
Type
Every other prop is forwarded to the Base UI OTPField.