Input OTP
Accessible one-time password component with copy-paste functionality, built on input-otp. Its slots always read left-to-right, even on RTL pages, since one-time codes are digits.
Last updated August 7, 2026
Overview
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"
export function InputOTPDemoExample() {
return (
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
)
}Installation
$ npx shadcn@latest add https://ui.persian-labs.ir/r/input-otp.jsonUsage
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"
export function Example() {
return (
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
)
}Pattern
Use the pattern prop to define a custom pattern for the OTP input.
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
REGEXP_ONLY_DIGITS_AND_CHARS,
} from "@/components/ui/input-otp"
export function InputOTPPatternExample() {
return (
<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
)
}Separator
Use <InputOTPSeparator /> to add a separator between input groups.
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"
export function InputOTPSeparatorExample() {
return (
<InputOTP maxLength={4}>
<InputOTPGroup>
<InputOTPSlot index={0} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={1} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
</InputOTPGroup>
</InputOTP>
)
}Disabled
Use the disabled prop to disable the input.
1
2
3
4
5
6
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp"
export function InputOTPDisabledExample() {
return (
<InputOTP maxLength={6} disabled value="123456">
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
)
}Controlled
Use the value and onChange props to control the input value.
Enter your code.
"use client"
import * as React from "react"
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp"
export function InputOTPControlledExample() {
const [value, setValue] = React.useState("")
return (
<div className="flex flex-col items-center gap-3">
<InputOTP maxLength={6} value={value} onChange={setValue}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
<p className="text-muted-foreground text-sm">
{value === "" ? "Enter your code." : `Value: ${value}`}
</p>
</div>
)
}Invalid
Use aria-invalid on the slots to show an error state.
1
2
3
4
5
6
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp"
export function InputOTPInvalidExample() {
return (
<InputOTP maxLength={6} value="123456">
<InputOTPGroup>
<InputOTPSlot index={0} aria-invalid />
<InputOTPSlot index={1} aria-invalid />
<InputOTPSlot index={2} aria-invalid />
<InputOTPSlot index={3} aria-invalid />
<InputOTPSlot index={4} aria-invalid />
<InputOTPSlot index={5} aria-invalid />
</InputOTPGroup>
</InputOTP>
)
}Four digits
A common pattern for PIN codes, using pattern={REGEXP_ONLY_DIGITS}.
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
REGEXP_ONLY_DIGITS,
} from "@/components/ui/input-otp"
export function InputOTPFourDigitsExample() {
return (
<InputOTP maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
</InputOTPGroup>
</InputOTP>
)
}Alphanumeric
Use REGEXP_ONLY_DIGITS_AND_CHARS to accept both letters and numbers.
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
REGEXP_ONLY_DIGITS_AND_CHARS,
} from "@/components/ui/input-otp"
export function InputOTPAlphanumericExample() {
return (
<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
)
}Form
"use client"
import { Button } from "@/components/ui/button"
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
} from "@/components/ui/field"
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp"
export function InputOTPFormExample() {
return (
<form className="w-full max-w-sm" onSubmit={(e) => e.preventDefault()}>
<FieldGroup>
<Field>
<FieldLabel htmlFor="input-otp-form-code">One-time code</FieldLabel>
<InputOTP maxLength={6} id="input-otp-form-code">
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
<FieldDescription>
Enter the 6-digit code sent to your phone.
</FieldDescription>
</Field>
<Button type="submit">Verify</Button>
</FieldGroup>
</form>
)
}API Reference
InputOTP
| Prop | Type | Default | Description |
|---|---|---|---|
| maxLength | number | required | The total number of characters the OTP can hold. |
| value | string | undefined | The value of the OTP input. Use when controlled. |
| onChange | (value: string) => void | — | Called when the OTP value changes. |
| pattern | string | RegExp | undefined | Restricts which characters are accepted, e.g. REGEXP_ONLY_DIGITS. |
| disabled | boolean | false | Prevents interaction with the input. |
InputOTPSlot
| Prop | Type | Default | Description |
|---|---|---|---|
| index | number | required | The zero-based position of this slot. |