Components
HeroUI v3 InputOTP — segmented one-time-password input: digit counts, patterns, disabled, controlled, validation, on-complete, form integration, variants and on-surface.
npx shadcn@latest add https://21st.dev/r/larsen66/heroui-input-otpLoading preview...
"use client"
import { InputOTP } from "@/components/ui/heroui-input-otp"
import { Button, Form, Label, Spinner } from "@heroui/react"
import React from "react"
export default function OnComplete() {
const [value, setValue] = React.useState("")
const [isComplete, setIsComplete] = React.useState(false)
const [isSubmitting, setIsSubmitting] = React.useState(false)
const handleComplete = (code: string) => {
setIsComplete(true)
console.log("Code complete:", code)
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setIsSubmitting(true)
// Simulate API call
setTimeout(() => {
setIsSubmitting(false)
setValue("")
setIsComplete(false)
}, 2000)
}
return (
<Form className="flex w-[280px] flex-col gap-2" onSubmit={handleSubmit}>
<Label>Verify account</Label>
<InputOTP
maxLength={6}
value={value}
onComplete={handleComplete}
onChange={(val) => {
setValue(val)
setIsComplete(false)
}}
>
<InputOTP.Group>
<InputOTP.Slot index={0} />
<InputOTP.Slot index={1} />
<InputOTP.Slot index={2} />
</InputOTP.Group>
<InputOTP.Separator />
<InputOTP.Group>
<InputOTP.Slot index={3} />
<InputOTP.Slot index={4} />
<InputOTP.Slot index={5} />
</InputOTP.Group>
</InputOTP>
<Button
className="mt-2 w-full"
isDisabled={!isComplete}
isPending={isSubmitting}
type="submit"
variant="primary"
>
{isSubmitting ? (
<>
<Spinner color="current" size="sm" />
Verifying...
</>
) : (
"Verify Code"
)}
</Button>
</Form>
)
}
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...
Loading preview...