Overview
The AxInputOTP component provides a specialized input for one-time passwords (OTP), verification codes, and PINs. It features individual character slots, auto-focus progression, and optional masking for sensitive inputs.
import { AxInputOTP } from "axmed-design-system"
import type { AxInputOTPProps, AxInputOTPSize } from "axmed-design-system"
Number of individual digit/character slots
size
AxInputOTPSize
default:"'md'"
Preset sizes:
"sm" - Small slots
"md" - Medium slots (default)
"lg" - Large slots
"xl" - Extra large 56px slots for hero-style verification flows
Mask the value for sensitive codes like PINs. Pass true for the default bullet (•), or a custom string character (e.g., "*").
Label text displayed above the OTP input
Helper text displayed below the OTP input in neutral color
Error message displayed below the OTP input in red. Also sets aria-invalid on the input group.
Whether the field is required. Adds a red asterisk to the label.
Controlled input value. Should be a string matching the length.
Uncontrolled default value
Whether the input is disabled
Callback fired when OTP value changes. Receives the complete value string.
Callback fired when all slots are filled. Useful for auto-submitting the form.
Additional CSS classes to apply
Type Definitions
export type AxInputOTPSize = "sm" | "md" | "lg" | "xl"
export type AxInputOTPProps = {
/**
* Number of individual digit/character slots.
* @default 6
*/
length?: number
/**
* Preset sizes: "sm", "md" (default), "lg", "xl".
* xl renders 56px slots for hero-style verification flows.
*/
size?: AxInputOTPSize
/**
* Mask the value — useful for PINs and sensitive codes.
* Pass `true` for the default bullet (•), or a custom string character.
*/
masked?: boolean | string
/** Label displayed above the OTP input. */
label?: string
/** Helper text displayed below the OTP input (neutral). */
hint?: string
/**
* Error message displayed below the OTP input in red.
* Also sets `aria-invalid` on the input group.
*/
error?: string
/**
* Whether the field is required.
* Adds a red asterisk to the label.
*/
required?: boolean
} & Omit<OTPProps, "size" | "mask" | "length">
Usage Examples
Basic OTP Input
import { AxInputOTP } from "axmed-design-system"
import { useState } from "react"
function Example() {
const [otp, setOtp] = useState("")
return (
<AxInputOTP
label="Verification Code"
value={otp}
onChange={setOtp}
hint="Enter the 6-digit code sent to your email"
/>
)
}
Custom Length
import { AxInputOTP } from "axmed-design-system"
function Example() {
return (
<AxInputOTP
label="PIN"
length={4}
masked
hint="Enter your 4-digit PIN"
/>
)
}
Auto-Submit on Completion
import { AxInputOTP } from "axmed-design-system"
import { useState } from "react"
function Example() {
const [loading, setLoading] = useState(false)
const handleFinish = async (code: string) => {
setLoading(true)
try {
await verifyCode(code)
// Navigate to next step
} catch (error) {
// Handle error
} finally {
setLoading(false)
}
}
return (
<AxInputOTP
label="Enter Verification Code"
onFinish={handleFinish}
disabled={loading}
/>
)
}
With Error State
import { AxInputOTP } from "axmed-design-system"
import { useState } from "react"
function Example() {
const [otp, setOtp] = useState("")
const [error, setError] = useState("")
const handleFinish = async (code: string) => {
const isValid = await validateCode(code)
if (!isValid) {
setError("Invalid code. Please try again.")
} else {
setError("")
// Proceed
}
}
return (
<AxInputOTP
label="Verification Code"
value={otp}
onChange={(value) => {
setOtp(value)
setError("") // Clear error on change
}}
onFinish={handleFinish}
error={error}
required
/>
)
}
Masked PIN Input
import { AxInputOTP } from "axmed-design-system"
function Example() {
return (
<>
{/* Default masking with bullet */}
<AxInputOTP
label="PIN"
length={4}
masked
hint="Enter your PIN"
/>
{/* Custom mask character */}
<AxInputOTP
label="Security Code"
length={6}
masked="*"
hint="Enter your security code"
/>
</>
)
}
Different Sizes
import { AxInputOTP } from "axmed-design-system"
function Example() {
return (
<>
<AxInputOTP size="sm" label="Small" />
<AxInputOTP size="md" label="Medium" />
<AxInputOTP size="lg" label="Large" />
<AxInputOTP size="xl" label="Extra Large (Hero)" />
</>
)
}
Hero Verification Flow
import { AxInputOTP } from "axmed-design-system"
import { AxText } from "axmed-design-system"
function Example() {
return (
<div style={{ textAlign: "center", maxWidth: 400, margin: "0 auto" }}>
<AxText variant="heading-2xl">Verify Your Email</AxText>
<AxText variant="body-md" color="secondary" style={{ marginTop: 8 }}>
We've sent a 6-digit code to your email address
</AxText>
<AxInputOTP
size="xl"
label="Verification Code"
onFinish={(code) => console.log("Code:", code)}
style={{ marginTop: 32 }}
/>
</div>
)
}
Uncontrolled with Default Value
import { AxInputOTP } from "axmed-design-system"
function Example() {
return (
<AxInputOTP
label="Pre-filled Code"
defaultValue="123456"
onFinish={(code) => console.log("Final code:", code)}
/>
)
}
Behavior
- Auto-focus progression: Automatically moves to the next slot as you type
- Backspace handling: Pressing backspace clears the current slot and moves to the previous one
- Paste support: Supports pasting complete codes from clipboard
- Keyboard navigation: Arrow keys work to move between slots
- Auto-completion: Triggers
onFinish callback when all slots are filled
Accessibility
- Wrapped in a
role="group" with proper aria-label from the label prop
- Error messages are linked via
aria-describedby
- Individual inputs are properly labeled for screen readers
aria-invalid is set when error state is present
- Required indicator (asterisk) is marked with
aria-hidden
Related Components
- AxInput - For standard text input fields
- AxModal - OTP inputs are commonly used in verification modals
- AxButton - For submit buttons in verification flows