Components
Loading preview...
A segmented input for one-time passwords and verification codes.
npx shadcn@latest add https://21st.dev/r/coss.com/input-otp"use client";
import { useId, useState } from "react";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/component";
function Label({ htmlFor, children, className, ...props }: React.LabelHTMLAttributes<HTMLLabelElement>) {
return (
<label
htmlFor={htmlFor}
className={`inline-flex items-center gap-2 font-medium text-sm text-foreground ${className ?? ""}`}
data-slot="label"
{...props}
>
{children}
</label>
);
}
export default function InputOTPAutoValidation() {
const id = useId();
const [value, setValue] = useState("");
const [invalid, setInvalid] = useState(false);
const valid = value.length === 6 && value === "123456";
return (
<div className="flex flex-col items-center gap-2">
<Label htmlFor={id}>Verification code</Label>
<InputOTP
aria-label="Verification code"
id={id}
maxLength={6}
onChange={(nextValue) => {
setValue(nextValue);
setInvalid(nextValue.length === 6 ? nextValue !== "123456" : false);
}}
value={value}
>
<InputOTPGroup>
<InputOTPSlot aria-invalid={invalid || undefined} index={0} />
<InputOTPSlot aria-invalid={invalid || undefined} index={1} />
<InputOTPSlot aria-invalid={invalid || undefined} index={2} />
<InputOTPSlot aria-invalid={invalid || undefined} index={3} />
<InputOTPSlot aria-invalid={invalid || undefined} index={4} />
<InputOTPSlot aria-invalid={invalid || undefined} index={5} />
</InputOTPGroup>
</InputOTP>
{!valid && !invalid && (
<p className="text-muted-foreground text-xs">
Enter `123456` to pass validation.
</p>
)}
{invalid && (
<p className="text-destructive text-xs">
Code must be 123456.
</p>
)}
{valid && (
<p className="text-green-500 text-xs">Code verified.</p>
)}
</div>
);
}