Installation

pnpm dlx shadcn@latest add @cortexcn/input-otpUsage
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "@/components/input-otp";Examples
Form
import { Button } from "@/components/button"import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/card"import { Field, FieldDescription, FieldLabel } from "@/components/field"import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "@/components/input-otp"import { RefreshCwIcon } from "lucide-react"export function InputOTPForm() { return ( <div className="flex flex-col gap-6"> <Card className="mx-auto max-w-md"> <CardHeader> <CardTitle>Verify your login</CardTitle> <CardDescription> Enter the verification code we sent to your email address:{" "} <span className="font-medium">m@example.com</span>. </CardDescription> </CardHeader> <CardContent> <form> <Field> <div className="flex items-center justify-between"> <FieldLabel htmlFor="otp-verification"> Verification code </FieldLabel> <Button variant="outline" size="xs"> <RefreshCwIcon data-icon="inline-start" /> Resend Code </Button> </div> <InputOTP maxLength={6} id="otp-verification" required> <InputOTPGroup className="*:data-[slot=input-otp-slot]:text-xl style-vega:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-maia:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-mira:*:data-[slot=input-otp-slot]:w-11 style-luma:*:data-[slot=input-otp-slot]:h-16 style-luma:*:data-[slot=input-otp-slot]:w-12 style-sera:*:data-[slot=input-otp-slot]:h-14 style-sera:*:data-[slot=input-otp-slot]:w-10"> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup className="*:data-[slot=input-otp-slot]:text-xl style-vega:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-maia:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-mira:*:data-[slot=input-otp-slot]:w-11 style-luma:*:data-[slot=input-otp-slot]:h-16 style-luma:*:data-[slot=input-otp-slot]:w-12 style-sera:*:data-[slot=input-otp-slot]:h-14 style-sera:*:data-[slot=input-otp-slot]:w-10"> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> <FieldDescription> <a href="#">I no longer have access to this email address.</a> </FieldDescription> </Field> </form> </CardContent> <CardFooter className="flex-col gap-2"> <Button type="submit" className="w-full"> Verify </Button> <div className="text-sm text-muted-foreground"> Having trouble signing in?{" "} <a href="#" className="underline underline-offset-4 transition-colors hover:text-primary" > Contact support </a> </div> </CardFooter> </Card> </div> )}Simple
import { Field, FieldLabel } from "@/components/field"import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "@/components/input-otp"export function InputOTPSimple() { return ( <div className="flex flex-col gap-6"> <Field> <FieldLabel htmlFor="simple">Simple</FieldLabel> <InputOTP id="simple" 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> </Field> </div> )}Digits only
import { REGEXP_ONLY_DIGITS } from "input-otp"import { Field, FieldLabel } from "@/components/field"import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/input-otp"export function InputOTPPattern() { return ( <div className="flex flex-col gap-6"> <Field> <FieldLabel htmlFor="digits-only">Digits Only</FieldLabel> <InputOTP id="digits-only" maxLength={6} pattern={REGEXP_ONLY_DIGITS}> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </div> )}With separator
1
2
3
4
5
6
import * as React from "react"import { Field, FieldLabel } from "@/components/field"import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "@/components/input-otp"export function InputOTPWithSeparator() { const [value, setValue] = React.useState("123456") return ( <div className="flex flex-col gap-6"> <Field> <FieldLabel htmlFor="with-separator">With Separator</FieldLabel> <InputOTP id="with-separator" maxLength={6} value={value} onChange={setValue} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </div> )}Alphanumeric
Accepts both letters and numbers.
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"import { Field, FieldDescription, FieldLabel } from "@/components/field"import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "@/components/input-otp"export function InputOTPAlphanumeric() { return ( <div className="flex flex-col gap-6"> <Field> <FieldLabel htmlFor="alphanumeric">Alphanumeric</FieldLabel> <FieldDescription>Accepts both letters and numbers.</FieldDescription> <InputOTP id="alphanumeric" maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </div> )}Disabled
1
2
3
4
5
6
import { Field, FieldLabel } from "@/components/field"import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "@/components/input-otp"export function InputOTPDisabled() { return ( <div className="flex flex-col gap-6"> <Field> <FieldLabel htmlFor="disabled">Disabled</FieldLabel> <InputOTP id="disabled" maxLength={6} disabled value="123456"> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </div> )}4 digits
Common pattern for PIN codes.
import { REGEXP_ONLY_DIGITS } from "input-otp"import { Field, FieldDescription, FieldLabel } from "@/components/field"import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/input-otp"export function InputOTPFourDigits() { return ( <div className="flex flex-col gap-6"> <Field> <FieldLabel htmlFor="four-digits">4 Digits</FieldLabel> <FieldDescription>Common pattern for PIN codes.</FieldDescription> <InputOTP id="four-digits" maxLength={4} pattern={REGEXP_ONLY_DIGITS}> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> </InputOTPGroup> </InputOTP> </Field> </div> )}Invalid state
Example showing the invalid error state.
0
0
0
0
0
0
Invalid code. Please try again.
import * as React from "react"import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/field"import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "@/components/input-otp"export function InputOTPInvalid() { const [value, setValue] = React.useState("000000") return ( <div className="flex flex-col gap-6"> <Field> <FieldLabel htmlFor="invalid">Invalid State</FieldLabel> <FieldDescription> Example showing the invalid error state. </FieldDescription> <InputOTP id="invalid" maxLength={6} value={value} onChange={setValue}> <InputOTPGroup> <InputOTPSlot index={0} aria-invalid /> <InputOTPSlot index={1} aria-invalid /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={2} aria-invalid /> <InputOTPSlot index={3} aria-invalid /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={4} aria-invalid /> <InputOTPSlot index={5} aria-invalid /> </InputOTPGroup> </InputOTP> <FieldError errors={[{ message: "Invalid code. Please try again." }]} /> </Field> </div> )}Adapted from the shadcn/ui Input OTP (MIT).
