OTP Input
A one-time-code input whose characters roll into place behind a caret that slides from slot to slot.
Enter 123456 to pass. Any other code fails.
Loading component source...
Installation
pnpm dlx shadcn@latest add mox/mox-ui/otp-inputRun this command from your project directory to add the component and its dependencies.
- 1
Install the packages
pnpm add motion clsx tailwind-merge - 2
Add the utility file
lib/utils.tsimport { clsx, type ClassValue } from "clsx";import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs));} - 3
Copy and paste the code into your project
Loading source⦠- 4
Update the import paths to match your project
Point
@/lib/utilsto your utility file if your path aliases differ.
Usage
import { useState } from "react"import OtpInput, { type OtpStatus } from "@/components/ui/otp-input"
export function Demo() { const [status, setStatus] = useState<OtpStatus>("idle")
return ( <OtpInput length={6} size="md" status={status} onChange={() => setStatus("idle")} onComplete={(code) => setStatus(checkCode(code) ? "success" : "error")} /> )}Options
Pick a value to see the prop you would pass. Defaults are left out.
<OtpInput />Props
| Prop | Type | Default | Description |
|---|---|---|---|
length | number | Default: 6 | How many boxes to render, so a 4 digit code is length={4}. Any count works. |
size | "sm" | "md" | "lg" | Default: "md" | Overall scale of the boxes. Maps to 40px (sm), 48px (md), and 56px (lg), and carries the text, caret, and gaps with it. |
value | string | Default: - | The current code. Pass it to control the input yourself; leave it out to let the component track its own state. |
defaultValue | string | Default: "" | Starting code when the input is uncontrolled. |
onChange | (value: string) => void | Default: - | Fires on every edit with the full code so far. |
onComplete | (value: string) => void | Default: - | Fires once the last slot is filled. |
type | "numbers" | "letters" | "both" | Default: "numbers" | Which characters a slot accepts. Anything else is ignored, including on paste. |
status | "idle" | "success" | "error" | Default: "idle" | Drives the feedback state. Success traces a green ring around each box in turn, error rings them red and shakes the row once. |
mask | boolean | Default: false | Hides the characters, like a password field. |
disabled | boolean | Default: false | Blocks input and dims every slot. |
autoFocus | boolean | Default: false | Focuses the first slot on mount. |
className | string | Default: - | Extra classes for the row that wraps the slots. |
slotClassName | string | Default: - | Extra classes for each slot, for sizing and colors. |
Type to fill each slot and move to the next one. Backspace clears a slot in place, then steps back on the next press. Arrow keys move between slots, and a caret slides along with you. Pasting a code, or letting the phone autofill one from a text message, drops it straight in. Set the status to turn the slots green, or shake them red on a wrong code.
Credits and license
Most components here are recreations of great work from around the web. I don't claim to be the original creator - this is my attempt to reverse-engineer, replicate, and often add a few extra features. I've tried to credit everyone; if I missed someone, let me know.
- Free to use and modify in both personal and commercial projects.
- Attribution to Mox UI is appreciated when using a component.
- Please don't resell the components as your own kit.