Docs/OTP Input
OTP Input Component
Accessible OTP input with copy-paste support and keyboard navigation.
Installation
npm install irismailPeer Dependencies
The OTP components require React 18+ and work best with Tailwind CSS.
Basic Usage
Import OTP from irismail/react and use it with controlled state.
components/verify-form.tsxtsx
'use client';
import { useState } from 'react';
import { OTP } from 'irismail/react';
export function VerifyForm() {
const [code, setCode] = useState('');
return (
<OTP
value={code}
onChange={setCode}
onComplete={(value) => console.log('Submitted:', value)}
/>
);
}Themes
Use the theme prop to switch between dark and light color schemes.
// Dark theme (default)
<OTP theme="dark" value={code} onChange={setCode} />
// Light theme - for light backgrounds
<OTP theme="light" value={code} onChange={setCode} />Sizes
Use the slotSize prop to change the slot dimensions.
// Small
<OTP slotSize="sm" value={code} onChange={setCode} />
// Medium (default)
<OTP slotSize="md" value={code} onChange={setCode} />
// Large
<OTP slotSize="lg" value={code} onChange={setCode} />Separator & Grouping
Use separator to add visual separators and groupSize to control grouping.
// With separator (auto groups: 3-3 for 6 digits)
<OTP separator value={code} onChange={setCode} />
// Custom group size (2-2-2 for 6 digits)
<OTP separator groupSize={2} value={code} onChange={setCode} />
// Stripe-style (3-3 for 6 digits)
<OTP length={6} separator groupSize={3} value={code} onChange={setCode} />Error State
Use the error prop to show validation errors.
components/validated-otp.tsxtsx
const [code, setCode] = useState('');
const [isInvalid, setIsInvalid] = useState(false);
const handleComplete = async (value: string) => {
const isValid = await verifyCode(value);
setIsInvalid(!isValid);
};
<OTP
value={code}
onChange={setCode}
onComplete={handleComplete}
error={isInvalid}
/>Custom Length
Use the length prop to change the number of digits.
// 4-digit OTP
<OTP length={4} value={code} onChange={setCode} />
// 8-digit OTP
<OTP length={8} value={code} onChange={setCode} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
length | number | 6 | Number of OTP digits |
value | string | — | Controlled input value |
onChange | (value: string) => void | — | Called when value changes |
onComplete | (value: string) => void | — | Called when all digits are entered |
theme | "dark" | "light" | "dark" | Color theme |
slotSize | "sm" | "md" | "lg" | "md" | Size of the input slots |
separator | boolean | false | Show separator between groups |
groupSize | number | length / 2 | Number of slots per group when separator is enabled |
disabled | boolean | false | Disable the input |
error | boolean | false | Show error styling |
autoFocus | boolean | false | Auto focus first slot on mount |
name | string | — | Name attribute for form integration |
className | string | — | Additional className for the container |
classNames | OTPClassNames | — | Styles API for fine-grained customization |
pattern | string | ^[0-9]*$ | Regex pattern to match input against |
Styles API
Use the classNames prop to customize individual parts of the component. This gives you full control over styling without having to rebuild the component from scratch.
| Prop | Type | Default | Description |
|---|---|---|---|
root | string | — | Root container element |
group | string | — | Group wrapper containing slots |
slot | string | — | Individual digit slot (base state) |
slotFilled | string | — | Slot when it contains a digit |
slotActive | string | — | Slot when focused/active |
slotError | string | — | Slot in error state |
separator | string | — | Separator container |
separatorLine | string | — | The separator dash/line element |
caret | string | — | Blinking caret cursor |
// Custom styling example
<OTP
value={code}
onChange={setCode}
separator
groupSize={3}
classNames={{
// Remove rounding except first/last slots
slot: "rounded-none first:rounded-l-md last:rounded-r-md",
// Custom focus ring
slotActive: "ring-2 ring-indigo-500 ring-offset-1 ring-offset-zinc-900",
// Custom separator styling
separator: "mx-4",
separatorLine: "bg-indigo-500 w-4 h-0.5",
// Custom caret color
caret: "bg-indigo-400",
}}
/>The OTP component is built on top of input-otp by @guilhermerodz.
Try it out
Check out the interactive playground or star us on GitHub.