InputOTP
A one-time password input component for verification codes and secure authentication
Import
import { InputOTP } from '@heroui/react';Usage
We've sent a code to a****@gmail.com
Didn't receive a code?
Resendimport {InputOTP, Label, Link} from "@heroui/react";
export function Basic() {
return (
<div className="flex w-[280px] flex-col gap-2">Anatomy
Import the InputOTP component and access all parts using dot notation.
import { InputOTP } from '@heroui/react';
export default () => (
<InputOTP maxLength={6}>
<InputOTP.Group>
<InputOTP.Slot index={0} />
<InputOTP.Slot index={1} />
{/* ...rest of the slots */}
</InputOTP.Group>
<InputOTP.Separator />
<InputOTP.Group>
<InputOTP.Slot index={3} />
{/* ...rest of the slots */}
</InputOTP.Group>
</InputOTP>
)InputOTP is built on top of input-otp by @guilherme_rodz, providing a flexible and accessible foundation for OTP input components.
Four Digits
import {InputOTP, Label} from "@heroui/react";
export function FourDigits() {
return (
<div className="flex w-[280px] flex-col gap-2">Disabled State
import {Description, InputOTP, Label} from "@heroui/react";
export function Disabled() {
return (
<div className="flex w-[280px] flex-col gap-2">With Pattern
Use the pattern prop to restrict input to specific characters. HeroUI exports common patterns like REGEXP_ONLY_CHARS and REGEXP_ONLY_DIGITS.
import {Description, InputOTP, Label, REGEXP_ONLY_CHARS} from "@heroui/react";
export function WithPattern() {
return (
<div className="flex w-[280px] flex-col gap-2">Controlled
Control the value to synchronize with state, clear the input, or implement custom validation.
"use client";
import {Description, InputOTP, Label} from "@heroui/react";
import React from "react";
With Validation
Use isInvalid together with validation messages to surface errors.
"use client";
import {Button, Description, Form, InputOTP, Label} from "@heroui/react";
import React from "react";
On Complete
Use the onComplete callback to trigger actions when all slots are filled.
"use client";
import {Button, Form, InputOTP, Label, Spinner} from "@heroui/react";
import React from "react";
Form Example
A complete two-factor authentication form with validation and submission.
"use client";
import {Button, Description, Form, InputOTP, Label, Link, Spinner} from "@heroui/react";
import React from "react";
On Surface
When used inside a Surface component, InputOTP automatically applies on-surface styling.
We've sent a code to a****@gmail.com
Didn't receive a code?
Resendimport {InputOTP, Label, Link, Surface} from "@heroui/react";
export function OnSurface() {
return (
<Surface className="flex w-full flex-col gap-2 rounded-3xl p-6">Styling
Passing Tailwind CSS classes
import {InputOTP, Label} from '@heroui/react';
function CustomInputOTP() {
return (
<div className="flex flex-col gap-2">
<Label className="text-sm font-semibold">Enter verification code</Label>
<InputOTP
className="gap-3"
containerClassName="gap-4"
maxLength={6}
>
<InputOTP.Group className="gap-3">
<InputOTP.Slot
className="size-12 rounded-lg border-2 text-lg font-bold"
index={0}
/>
<InputOTP.Slot
className="size-12 rounded-lg border-2 text-lg font-bold"
index={1}
/>
<InputOTP.Slot
className="size-12 rounded-lg border-2 text-lg font-bold"
index={2}
/>
</InputOTP.Group>
<InputOTP.Separator className="bg-border h-1 w-2 rounded-full" />
<InputOTP.Group className="gap-3">
<InputOTP.Slot
className="size-12 rounded-lg border-2 text-lg font-bold"
index={3}
/>
<InputOTP.Slot
className="size-12 rounded-lg border-2 text-lg font-bold"
index={4}
/>
<InputOTP.Slot
className="size-12 rounded-lg border-2 text-lg font-bold"
index={5}
/>
</InputOTP.Group>
</InputOTP>
</div>
);
}Customizing the component classes
To customize the InputOTP component classes, you can use the @layer components directive.
Learn more.
@layer components {
.input-otp {
@apply gap-3;
}
.input-otp__slot {
@apply size-12 rounded-xl border-2 font-bold;
}
.input-otp__slot[data-active="true"] {
@apply border-primary-500 ring-2 ring-primary-200;
}
.input-otp__separator {
@apply w-2 h-1 bg-border-strong rounded-full;
}
}HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
The InputOTP component uses these CSS classes (View source styles):
Base Classes
.input-otp- Base container.input-otp__container- Inner container from input-otp library.input-otp__group- Group of slots.input-otp__slot- Individual input slot.input-otp__slot-value- The character inside a slot.input-otp__caret- Blinking caret indicator.input-otp__separator- Visual separator between groups
State Classes
.input-otp__slot[data-active="true"]- Currently active slot.input-otp__slot[data-filled="true"]- Slot with a character.input-otp__slot[data-disabled="true"]- Disabled slot.input-otp__slot[data-invalid="true"]- Invalid slot.input-otp__container[data-disabled="true"]- Disabled container
Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
- Hover:
:hoveror[data-hovered="true"]on slot - Active:
[data-active="true"]on slot (currently focused) - Filled:
[data-filled="true"]on slot (contains a character) - Disabled:
[data-disabled="true"]on container and slots - Invalid:
[data-invalid="true"]on slots
API Reference
InputOTP Props
InputOTP is built on top of the input-otp library with additional features.
Base Props
| Prop | Type | Default | Description |
|---|---|---|---|
maxLength | number | - | Required. Number of input slots. |
value | string | - | Controlled value (uncontrolled if not provided). |
onChange | (value: string) => void | - | Handler called when the value changes. |
onComplete | (value: string) => void | - | Handler called when all slots are filled. |
className | string | - | Additional CSS classes for the container. |
containerClassName | string | - | CSS classes for the inner container. |
isOnSurface | boolean | false | Whether the input is displayed on a surface (affects styling) |
children | React.ReactNode | - | InputOTP.Group, InputOTP.Slot, and InputOTP.Separator components. |
Validation Props
| Prop | Type | Default | Description |
|---|---|---|---|
isDisabled | boolean | false | Whether the input is disabled. |
isInvalid | boolean | false | Whether the input is in an invalid state. |
validationErrors | string[] | - | Server-side or custom validation errors. |
validationDetails | ValidityState | - | HTML5 validation details. |
Input Props
| Prop | Type | Default | Description |
|---|---|---|---|
pattern | string | - | Regex pattern for allowed characters (e.g., REGEXP_ONLY_DIGITS). |
textAlign | 'left' | 'center' | 'right' | 'left' | Text alignment within slots. |
inputMode | 'numeric' | 'text' | 'decimal' | 'tel' | 'search' | 'email' | 'url' | 'numeric' | Virtual keyboard type on mobile devices. |
placeholder | string | - | Placeholder text for empty slots. |
pasteTransformer | (text: string) => string | - | Transform pasted text (e.g., remove hyphens). |
Form Props
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | Name attribute for form submission. |
autoFocus | boolean | - | Whether to focus the first slot on mount. |
InputOTP.Group Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes for the group. |
children | React.ReactNode | - | InputOTP.Slot components. |
InputOTP.Slot Props
| Prop | Type | Default | Description |
|---|---|---|---|
index | number | - | Required. Zero-based index of the slot. |
className | string | - | Additional CSS classes for the slot. |
InputOTP.Separator Props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes for the separator. |
Exported Patterns
HeroUI re-exports common regex patterns from input-otp for convenience:
import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS_AND_CHARS } from '@heroui/react';
// Use with 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)





