shadcn.io is an independent project built on shadcn/ui
Six boxes in two groups of three. After a full code, a spinner and the words Verifying code... appear under the boxes.
Show the spinner while the check is in flight so the pause does not look like a failure. A Button label can say the same thing if you submit by hand. In React, Base UI does not verify the code.
Installation
Anatomy

Usage
<div className="flex flex-col gap-2">
<InputOTP
maxLength={6}
onChange={handleValueChange}
onComplete={handleComplete}
value={value}
>
<InputOTPGroup>
<InputOTPSlot className="bg-background" index={0} />
<InputOTPSlot className="bg-background" index={1} />
<InputOTPSlot className="bg-background" index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot className="bg-background" index={3} />
<InputOTPSlot className="bg-background" index={4} />
<InputOTPSlot className="bg-background" index={5} />
</InputOTPGroup>
</InputOTP>
{isVerifying ? (
<div className="flex items-center gap-2 text-sm" role="status">
<Spinner />
<span>Verifying code...</span>
</div>
) : null}
</div>FAQ
What starts the verification?
The field's completion callback, which fires once every slot is filled.
onComplete fires when the code reaches maxLength.
How is the loading state shown?
A row with our Spinner and the text Verifying code..., rendered only while verifying. The row has role status, so the message is announced.
Is there a real request?
No. A two-second timer stands in for it. Replace it with your API call and turn the loading state off when it settles.
Should the field be locked while verifying?
Often, yes: pass disabled from the same loading state so the code can't change mid-request. The Success State example disables the field once the code is accepted.
API Reference
Input OTP
Sets maxLength={6}, onChange={handleValueChange}, onComplete={handleComplete}, value={value}, index={0} and index={1}. The rest is on the Input OTP page.
Spinner
No extra props in this demo. The rest is on the Spinner page.





