OTP Input
FormOne-time password input with individual digit boxes.
Preview
Enter verification code
Usage
example.jsx
import { OTPInput } from "@/components/ui/otp-input";
export default function Example() {
return <OTPInput />;
}Source Code
Copy this file into components/ui/otp-input.jsx in your project.
otp-input.jsx
"use client";
import { forwardRef, useRef } from "react";
import { cn } from "@/lib/utils";
const OTPInput = forwardRef(({ className, length = 6, value = "", onValueChange, ...props }, ref) => {
const inputsRef = useRef([]);
const handleChange = (index, e) => {
const val = e.target.value.replace(/[^0-9]/g, "");
if (!val) return;
const arr = (value || "").split("");
arr[index] = val[val.length - 1];
const next = arr.join("").slice(0, length);
onValueChange?.(next);
if (index < length - 1 && val) inputsRef.current[index + 1]?.focus();
};
const handleKeyDown = (index, e) => {
if (e.key === "Backspace" && !e.target.value && index > 0) {
inputsRef.current[index - 1]?.focus();
}
};
return (
<div ref={ref} className={cn("flex items-center gap-2", className)} {...props}>
{Array.from({ length }).map((_, i) => (
<input
key={i}
ref={(el) => (inputsRef.current[i] = el)}
type="text"
inputMode="numeric"
maxLength={1}
value={(value || "")[i] || ""}
onChange={(e) => handleChange(i, e)}
onKeyDown={(e) => handleKeyDown(i, e)}
className="flex h-10 w-10 items-center justify-center rounded-md border border-input bg-transparent text-center text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
/>
))}
</div>
);
});
OTPInput.displayName = "OTPInput";
export { OTPInput };
Quick Install
Make sure you have the cn() utility set up. It requires clsx and tailwind-merge.
npm install clsx tailwind-merge