Input OTP
---import { InputOtp, InputOtpGroup, InputOtpSlot,} from "@/components/starwind/input-otp";---
<InputOtp maxLength={6}> <InputOtpGroup> <InputOtpSlot index={0} /> <InputOtpSlot index={1} /> <InputOtpSlot index={2} /> <InputOtpSlot index={3} /> <InputOtpSlot index={4} /> <InputOtpSlot index={5} /> </InputOtpGroup></InputOtp>Installation
pnpx starwind@latest add input-otpnpx starwind@latest add input-otpyarn dlx starwind@latest add input-otpUsage
General Notes
The Input OTP component provides a user-friendly way to input one-time passwords and verification codes. It supports keyboard navigation, paste handling, and pattern validation.
The essential components are InputOtp, InputOtpGroup, and InputOtpSlot. The InputOtpSeparator provides visual separation between groups of slots.
With Separator
Use InputOtpSeparator to visually group slots, commonly used for phone verification codes or formatted inputs.
---import { InputOtp, InputOtpGroup, InputOtpSeparator, InputOtpSlot,} from "@/components/starwind/input-otp";---
<InputOtp maxLength={6}> <InputOtpGroup> <InputOtpSlot index={0} /> <InputOtpSlot index={1} /> <InputOtpSlot index={2} /> </InputOtpGroup> <InputOtpSeparator /> <InputOtpGroup> <InputOtpSlot index={3} /> <InputOtpSlot index={4} /> <InputOtpSlot index={5} /> </InputOtpGroup></InputOtp>Alphanumeric Pattern
Use the pattern prop with REGEXP_ONLY_DIGITS_AND_CHARS to allow both letters and numbers.
---import { InputOtp, InputOtpGroup, InputOtpSlot, REGEXP_ONLY_DIGITS_AND_CHARS,} from "@/components/starwind/input-otp";---
<InputOtp maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}> <InputOtpGroup> <InputOtpSlot index={0} /> <InputOtpSlot index={1} /> <InputOtpSlot index={2} /> <InputOtpSlot index={3} /> <InputOtpSlot index={4} /> <InputOtpSlot index={5} /> </InputOtpGroup></InputOtp>Sizes
The InputOtpSlot component supports three sizes: sm, md (default), and lg.
Small
Medium (default)
Large
---import { InputOtp, InputOtpGroup, InputOtpSlot,} from "@/components/starwind/input-otp";---
<div class="flex flex-col gap-6"> <div class="space-y-2"> <p class="text-muted-foreground text-sm">Small</p> <InputOtp maxLength={4}> <InputOtpGroup> <InputOtpSlot index={0} size="sm" /> <InputOtpSlot index={1} size="sm" /> <InputOtpSlot index={2} size="sm" /> <InputOtpSlot index={3} size="sm" /> </InputOtpGroup> </InputOtp> </div> <div class="space-y-2"> <p class="text-muted-foreground text-sm">Medium (default)</p> <InputOtp maxLength={4}> <InputOtpGroup> <InputOtpSlot index={0} size="md" /> <InputOtpSlot index={1} size="md" /> <InputOtpSlot index={2} size="md" /> <InputOtpSlot index={3} size="md" /> </InputOtpGroup> </InputOtp> </div> <div class="space-y-2"> <p class="text-muted-foreground text-sm">Large</p> <InputOtp maxLength={4}> <InputOtpGroup> <InputOtpSlot index={0} size="lg" /> <InputOtpSlot index={1} size="lg" /> <InputOtpSlot index={2} size="lg" /> <InputOtpSlot index={3} size="lg" /> </InputOtpGroup> </InputOtp> </div></div>Disabled
Use the disabled prop to prevent user interaction.
---import { InputOtp, InputOtpGroup, InputOtpSlot,} from "@/components/starwind/input-otp";---
<InputOtp maxLength={6} disabled> <InputOtpGroup> <InputOtpSlot index={0} /> <InputOtpSlot index={1} /> <InputOtpSlot index={2} /> <InputOtpSlot index={3} /> <InputOtpSlot index={4} /> <InputOtpSlot index={5} /> </InputOtpGroup></InputOtp>Form Submission
The component integrates with native HTML forms using the name prop. The hidden input stores the complete OTP value.
---import { Button } from "@/components/starwind/button";import { InputOtp, InputOtpGroup, InputOtpSlot,} from "@/components/starwind/input-otp";import { Label } from "@/components/starwind/label";---
<form id="otp-demo-form" class="space-y-4"> <div class="space-y-2"> <Label for="otp-form">Verification Code</Label> <InputOtp id="otp-form" name="otp" maxLength={6} required> <InputOtpGroup> <InputOtpSlot index={0} /> <InputOtpSlot index={1} /> <InputOtpSlot index={2} /> <InputOtpSlot index={3} /> <InputOtpSlot index={4} /> <InputOtpSlot index={5} /> </InputOtpGroup> </InputOtp> </div> <Button type="submit">Submit Code</Button></form><div id="form-result" class="bg-muted mt-4 hidden rounded-md p-4 font-mono text-sm"></div>
<script> const form = document.getElementById("otp-demo-form") as HTMLFormElement; const resultDiv = document.getElementById("form-result");
if (form && resultDiv) { form.addEventListener("submit", (e) => { e.preventDefault(); const formData = new FormData(form); const otp = formData.get("otp");
resultDiv.textContent = `Submitted OTP: ${otp}`; resultDiv.classList.remove("hidden"); }); }</script>API Reference
InputOtp
The root container that manages the OTP input state and keyboard interactions.
| Prop | Type | Default |
|---|---|---|
maxLength | number | 6 |
value | string | - |
defaultValue | string | - |
disabled | boolean | false |
pattern | RegExp | string | \d |
name | string | - |
id | string | - |
required | boolean | - |
class | string | - |
<InputOtp maxLength={6} name="otp" pattern={REGEXP_ONLY_DIGITS_AND_CHARS}> <InputOtpGroup> <InputOtpSlot index={0} /> </InputOtpGroup></InputOtp>Additional Notes:
maxLength: The total number of characters allowed in the OTPpattern: A regex pattern to validate each character. UseREGEXP_ONLY_DIGITS_AND_CHARSfor alphanumeric orREGEXP_ONLY_DIGITSfor numbers onlyname: Required for form submission, the value is stored in a hidden inputdisabled: When true, prevents all user interaction
InputOtpGroup
A container for grouping slots together visually.
| Prop | Type | Default |
|---|---|---|
class | string | - |
<InputOtpGroup> <InputOtpSlot index={0} /> <InputOtpSlot index={1} /> <InputOtpSlot index={2} /></InputOtpGroup>InputOtpSlot
An individual input slot that displays a single character.
| Prop | Type | Default |
|---|---|---|
index | number | - |
size | "sm" | "md" | "lg" | "md" |
class | string | - |
<InputOtpSlot index={0} size="lg" />Additional Notes:
index: The position of this slot in the OTP sequence (0-indexed)size: Controls the slot dimensions. Use"sm"for compact layouts or"lg"for emphasis
InputOtpSeparator
A visual separator between groups of slots.
| Prop | Type | Default |
|---|---|---|
class | string | - |
<InputOtpSeparator />The separator renders a dash icon by default. Use the icon slot to customize:
<InputOtpSeparator> <span slot="icon">/</span></InputOtpSeparator>Exported Constants
| Name | Description |
|---|---|
REGEXP_ONLY_DIGITS | Pattern that allows only digits (0-9) |
REGEXP_ONLY_DIGITS_AND_CHARS | Pattern that allows alphanumeric characters (A-Z, a-z, 0-9) |
Events
The component dispatches a custom event when the value changes:
interface InputOtpChangeEvent { detail: { value: string; inputOtpId: string; };}Listen for changes:
<script> document.addEventListener("starwind-input-otp:change", (e) => { const { value, inputOtpId } = e.detail; console.log(`OTP ${inputOtpId} changed to: ${value}`); });</script>Changelog
v1.0.1
- Auto focus hidden input on click to automatically open keyboards on mobile devices
- Update component to always focus the next empty index on click
v1.0.0
- Initial release with starwind v1.15.0