input otp
Accessible one-time password component with paste-to-fill support.
Philosophy
OTP inputs are deceptively complex: they need to handle paste, auto-fill, focus management across segments, and mobile keyboard optimization. We isolate this complexity in a dedicated component rather than stretching Input to cover it. Each slot is its own element, giving you full control over styling and animation per digit.
How It's Built
Installation
npx @gentleduck/cli add input-otp
npx @gentleduck/cli add input-otp
Usage
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot />
<InputOTPSlot />
<InputOTPSlot />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot />
<InputOTPSlot />
<InputOTPSlot />
</InputOTPGroup>
</InputOTP><InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot />
<InputOTPSlot />
<InputOTPSlot />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot />
<InputOTPSlot />
<InputOTPSlot />
</InputOTPGroup>
</InputOTP>Paste
- Pasting fills slots starting at the focused input.
- Characters are filtered by the
patternprop. onValueChangefires after paste and per-key entry.
Examples
Pattern
Use the pattern prop to define a custom pattern for the OTP input.
...
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot />
{/* ... */}
</InputOTPGroup>
</InputOTP>
...
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot />
{/* ... */}
</InputOTPGroup>
</InputOTP>Separator
You can use the <InputOTPSeparator /> component to add a separator between the input groups.
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"
...
<InputOTP maxLength={4}>
<InputOTPGroup>
<InputOTPSlot />
<InputOTPSlot />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot />
<InputOTPSlot />
</InputOTPGroup>
</InputOTP>import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"
...
<InputOTP maxLength={4}>
<InputOTPGroup>
<InputOTPSlot />
<InputOTPSlot />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot />
<InputOTPSlot />
</InputOTPGroup>
</InputOTP>Controlled
You can use the value and onValueChange props to control the input value.
Custom separator
Form
API Reference
InputOTP
The root component that provides OTP context and manages slot focus, paste, and value state.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | -- | Additional class names for the wrapper div |
children | React.ReactNode | -- | OTP groups, slots, and separators |
value | string | -- | Controlled value of the OTP input |
onValueChange | (value: string) => void | -- | Callback fired when the combined slot value changes |
pattern | RegExp | /^.$/ | Regex used to validate each entered character |
maxLength | number | -- | Optional cap for active slots used by keyboard and paste behavior |
name | string | -- | Optional field name (useful with form libraries) |
dir | 'ltr' | 'rtl' | inherited | Local direction override |
ref | React.Ref<HTMLDivElement> | -- | Ref forwarded to the wrapper div |
aria-label | string | "otp-one-time-password" | Accessible label for the OTP region |
...props | Omit<React.HTMLProps<HTMLDivElement>, 'pattern'> | -- | Additional props to spread to the content div |
InputOTPGroup
Groups related OTP slots together with flex layout.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | -- | Additional class names for the group div |
children | React.ReactNode | -- | OTP slots to group together |
ref | React.Ref<HTMLDivElement> | -- | Ref forwarded to the group div |
...props | React.HTMLProps<HTMLDivElement> | -- | Additional props to spread to the content div |
InputOTPSlot
An individual input element representing a single OTP digit.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | -- | Additional class names for the input element |
ref | React.Ref<HTMLInputElement> | -- | Ref forwarded to the input element |
...props | React.HTMLProps<HTMLInputElement> | -- | Additional props to spread to the input element |
InputOTPSeparator
A visual separator rendered between OTP groups.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | -- | Additional class names for the separator div |
customIndicator | React.ReactNode | <Dot /> | Custom element to render instead of the default dot icon |
ref | React.Ref<HTMLDivElement> | -- | Ref forwarded to the separator div |
...props | React.HTMLProps<HTMLDivElement> | -- | Additional props to spread to the content div |
RTL Support
Direction is resolved through the shared primitives direction module. Use a local dir="rtl" override when the component exposes it, or set DirectionProvider at app/root level for global RTL/LTR behavior.