shadcn.io is an independent project built on shadcn/ui
Six boxes in two groups of three. Under them a line starts at Resend code in 60s, and Resend Code stays disabled until the count ends.
Hold the resend so it cannot be hammered. Send people to a Password Input when they should sign in with a password instead. In React, Ark UI moves to the next box as you type.
Installation
Anatomy

Usage
<div className="flex flex-col gap-4">
<InputOTP onValueChange={handleValueChange} 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>
<div className="flex items-center justify-between gap-4 text-sm">
<span className="text-muted-foreground">
{canResend
? "Didn’t receive the code?"
: `Resend code in ${countdown}s`}
</span>
<Button
className="h-auto p-0"
disabled={!canResend}
onClick={handleResend}
size="sm"
type="button"
variant="link"
>
Resend Code
</Button>
</div>
</div>FAQ
How does the countdown work?
The seconds left live in state, starting at 60. An effect schedules a one-second timer that lowers it by one and clears the timer on cleanup, so it stops at zero and never leaks.
When can people resend?
Only at zero. Until then the link-style Button is disabled and the text reads Resend code in 42s; at zero it reads Didn’t receive the code? and the button works.
What does Resend Code do?
It clears the code and restarts the countdown. Call your API there to send a new code.
How is the resend button styled?
variant link, size sm, and h-auto p-0 so it sits on the text line. It's type button, so it never submits a form.
API Reference
Input OTP
Sets onValueChange={handleValueChange}, value={value}, index={0}, index={1}, index={2} and index={3}. The rest is on the Input OTP page.
Button
Sets disabled={!canResend}, onClick={handleResend}, size="sm", type="button" and variant="link". The rest is on the Button page.





