InputOTP
Migration guide for InputOTP from HeroUI v2 to v3
Refer to the v3 InputOTP documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
Structure Changes
In v2, InputOtp automatically rendered segments based on the length prop:
import { InputOtp } from "@heroui/react";
export default function App() {
return <InputOtp length={4} />;
}In v3, InputOTP requires manual definition of slots using compound components:
import { InputOTP } from "@heroui/react";
export default function App() {
return (
<InputOTP maxLength={4}>
<InputOTP.Group>
<InputOTP.Slot index={0} />
<InputOTP.Slot index={1} />
<InputOTP.Slot index={2} />
<InputOTP.Slot index={3} />
</InputOTP.Group>
</InputOTP>
);
}Key Changes
1. Component Structure
v2: Single component with automatic segment rendering
v3: Compound components: InputOTP.Group, InputOTP.Slot, InputOTP.Separator
2. Prop Changes
| v2 Prop | v3 Location | Notes |
|---|---|---|
length | InputOTP | Renamed to maxLength |
allowedKeys | InputOTP | Renamed to pattern (regex) |
onValueChange | InputOTP | Use onChange |
description, errorMessage | - | Handle with Description / FieldError |
variant | InputOTP | Simplified to primary | secondary only |
color, size, radius | - | Removed (use Tailwind CSS) |
classNames | - | Use className on parts |
| - | textAlign | New prop: text alignment within slots ('left' | 'center' | 'right') |
| - | inputMode | New prop: virtual keyboard type on mobile (default 'numeric') |
| - | placeholder | New prop: placeholder text for empty slots |
| - | pasteTransformer | New prop: transform pasted text (e.g., remove hyphens) |
Migration Examples
Controlled InputOTP
import { useState } from "react";
const [value, setValue] = useState("");
<InputOtp length={4} value={value} onValueChange={setValue} />import { useState } from "react";
const [value, setValue] = useState("");
<InputOTP maxLength={4} value={value} onChange={setValue}>
<InputOTP.Group>
<InputOTP.Slot index={0} />
<InputOTP.Slot index={1} />
<InputOTP.Slot index={2} />
<InputOTP.Slot index={3} />
</InputOTP.Group>
</InputOTP>With Allowed Keys / Pattern
<InputOtp allowedKeys="^[a-z]*$" length={4} />import { REGEXP_ONLY_CHARS } from "@heroui/react";
<InputOTP maxLength={4} pattern={REGEXP_ONLY_CHARS}>
<InputOTP.Group>
<InputOTP.Slot index={0} />
<InputOTP.Slot index={1} />
<InputOTP.Slot index={2} />
<InputOTP.Slot index={3} />
</InputOTP.Group>
</InputOTP>Form Validation
{/* With description */}
<InputOtp description="Enter the code sent to your email" length={4} />
{/* With error message */}
<InputOtp errorMessage="Invalid code" isInvalid length={4} />import { Description, FieldError } from "@heroui/react";
{/* With description */}
<div className="flex flex-col gap-2">
<InputOTP maxLength={4}>
<InputOTP.Group>
<InputOTP.Slot index={0} />
<InputOTP.Slot index={1} />
<InputOTP.Slot index={2} />
<InputOTP.Slot index={3} />
</InputOTP.Group>
</InputOTP>
<Description>Enter the code sent to your email</Description>
</div>
{/* With error message */}
<div className="flex flex-col gap-2">
<InputOTP isInvalid maxLength={4}>
<InputOTP.Group>
<InputOTP.Slot index={0} />
<InputOTP.Slot index={1} />
<InputOTP.Slot index={2} />
<InputOTP.Slot index={3} />
</InputOTP.Group>
</InputOTP>
<FieldError>Invalid code</FieldError>
</div>With onComplete Callback
<InputOtp
length={6}
onComplete={(value) => console.log("Complete:", value)}
/><InputOTP
maxLength={6}
onComplete={(value) => console.log("Complete:", value)}
>
<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>Component Anatomy
The v3 InputOTP follows this structure:
InputOTP (Root)
├── InputOTP.Group
│ ├── InputOTP.Slot (index={0})
│ ├── InputOTP.Slot (index={1})
│ └── ...
├── InputOTP.Separator (optional)
└── InputOTP.Group (optional, for grouping)
└── InputOTP.Slot (index={...})New Props in v3
v3 introduces several new props not available in v2:
textAlign: Controls text alignment within slots ('left'|'center'|'right', default'left')inputMode: Sets the virtual keyboard type on mobile devices ('numeric'|'text'|'decimal'|'tel'|'search'|'email'|'url', default'numeric')placeholder: Sets placeholder text for empty slotspasteTransformer: A function(text: string) => stringto transform pasted text (e.g., removing hyphens from a pasted code)
<InputOTP
maxLength={6}
inputMode="numeric"
textAlign="center"
placeholder="-"
pasteTransformer={(text) => text.replace(/-/g, "")}
>
<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>Exported Regex Patterns
HeroUI re-exports common regex patterns from the input-otp library for convenience:
import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS_AND_CHARS } from "@heroui/react";
// Use with the pattern prop
<InputOTP pattern={REGEXP_ONLY_DIGITS} maxLength={6}>
{/* ... */}
</InputOTP>REGEXP_ONLY_DIGITS- Only numeric characters (0-9)REGEXP_ONLY_CHARS- Only alphabetic characters (a-z, A-Z)REGEXP_ONLY_DIGITS_AND_CHARS- Alphanumeric characters (0-9, a-z, A-Z)
Summary
- Component Structure: Must manually define slots using
InputOTP.GroupandInputOTP.Slot - length → maxLength: Prop renamed
- allowedKeys → pattern: Prop renamed, uses regex pattern
- onValueChange → onChange: Event handler renamed
- Description Removed: Handle separately with
Descriptioncomponent - Error Message Removed: Handle separately with error display
- Variant Simplified: v3 supports only
variant="primary"andvariant="secondary" - Colors Removed: Use Tailwind CSS classes for styling
- Sizes Removed: Use Tailwind CSS classes for styling
- Radius Removed: Use Tailwind CSS classes for styling
- ClassNames Removed: Use
classNameprops on individual components - New Props: v3 adds
textAlign,inputMode,placeholder, andpasteTransformerprops - Exported Patterns: Use
REGEXP_ONLY_DIGITS,REGEXP_ONLY_CHARS, andREGEXP_ONLY_DIGITS_AND_CHARSfor thepatternprop