InputOTPNew

Input component for entering one-time passwords (OTP) with individual character slots, animations, and validation support.

Import

import { InputOTP } from 'heroui-native';

Anatomy

<InputOTP>
  <InputOTP.Group>
    <InputOTP.Slot index={0} />
    <InputOTP.Slot index={1} />
  </InputOTP.Group>
  <InputOTP.Separator />
  <InputOTP.Group>
    <InputOTP.Slot index={2}>
      <InputOTP.SlotPlaceholder />
      <InputOTP.SlotValue />
      <InputOTP.SlotCaret />
    </InputOTP.Slot>
  </InputOTP.Group>
</InputOTP>
  • InputOTP: Main container that manages OTP input state, handles text changes, and provides context to child components. Manages focus, validation, and character input.
  • InputOTP.Group: Container for grouping multiple slots together. Use this to visually group related slots (e.g., groups of 3 digits).
  • InputOTP.Slot: Individual slot that displays a single character or placeholder. Each slot must have a unique index matching its position in the OTP sequence. When no children are provided, automatically renders SlotPlaceholder, SlotValue, and SlotCaret.
  • InputOTP.SlotPlaceholder: Text component that displays the placeholder character for a slot when it's empty. Used by default in Slot if no children provided.
  • InputOTP.SlotValue: Text component that displays the actual character value for a slot with animations. Used by default in Slot if no children provided.
  • InputOTP.SlotCaret: Animated caret indicator that shows the current input position. Place this inside a Slot to show where the user is currently typing.
  • InputOTP.Separator: Visual separator between groups of slots. Use this to visually separate different groups of OTP digits.

Usage

Basic Usage

Create a 6-digit OTP input with grouped slots and separator.

<InputOTP maxLength={6} onComplete={(code) => console.log(code)}>
  <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>

Four Digits

Create a simple 4-digit PIN input.

<InputOTP maxLength={4} onComplete={(code) => console.log(code)}>
  <InputOTP.Slot index={0} />
  <InputOTP.Slot index={1} />
  <InputOTP.Slot index={2} />
  <InputOTP.Slot index={3} />
</InputOTP>

With Placeholder

Provide custom placeholder characters for each slot position.

<InputOTP
  maxLength={6}
  placeholder="——————"
  onComplete={(code) => console.log(code)}
>
  <InputOTP.Group>
    {({ slots }) => (
      <>
        {slots.map((slot) => (
          <InputOTP.Slot key={slot.index} index={slot.index} />
        ))}
      </>
    )}
  </InputOTP.Group>
</InputOTP>

Controlled Value

Control the OTP value programmatically.

const [value, setValue] = useState('');

<InputOTP value={value} onChange={setValue} maxLength={6}>
  <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>;

With Validation

Display validation errors when the OTP is invalid.

<InputOTP value={value} onChange={setValue} maxLength={6} isInvalid={isInvalid}>
  <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>

With Pattern

Restrict input to specific character patterns using regex. Three predefined patterns are available: REGEXP_ONLY_DIGITS (matches digits 0-9), REGEXP_ONLY_CHARS (matches alphabetic characters a-z, A-Z), and REGEXP_ONLY_DIGITS_AND_CHARS (matches both digits and alphabetic characters).

import { InputOTP, REGEXP_ONLY_CHARS } from 'heroui-native';

<InputOTP
  maxLength={6}
  pattern={REGEXP_ONLY_CHARS}
  inputMode="text"
  onComplete={(code) => console.log(code)}
>
  <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>;

Custom Layout

Use render props in Group to create custom slot layouts.

<InputOTP maxLength={6}>
  <InputOTP.Group>
    {({ slots, isFocused, isInvalid }) => (
      <>
        {slots.map((slot) => (
          <InputOTP.Slot
            key={slot.index}
            index={slot.index}
            className={cn('custom-class', slot.isActive && 'active-class')}
          >
            <InputOTP.SlotPlaceholder />
            <InputOTP.SlotValue />
            <InputOTP.SlotCaret />
          </InputOTP.Slot>
        ))}
      </>
    )}
  </InputOTP.Group>
</InputOTP>

Example

import { InputOTP, Label, Description, type InputOTPRef } from 'heroui-native';
import { View } from 'react-native';
import { useRef } from 'react';

export default function InputOTPExample() {
  const ref = useRef<InputOTPRef>(null);

  const onComplete = (code: string) => {
    console.log('OTP completed:', code);
    setTimeout(() => {
      ref.current?.clear();
    }, 1000);
  };

  return (
    <View className="flex-1 px-5 items-center justify-center">
      <View>
        <Label>Verify account</Label>
        <Description className="mb-3">
          We've sent a code to a****@gmail.com
        </Description>
        <InputOTP ref={ref} maxLength={6} onComplete={onComplete}>
          <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>
      </View>
    </View>
  );
}

You can find more examples in the GitHub repository.

API Reference

InputOTP

proptypedefaultdescription
maxLengthnumber-Maximum length of the OTP (required)
valuestring-Controlled value for the OTP input
defaultValuestring-Default value for uncontrolled usage
onChange(value: string) => void-Callback when value changes
onComplete(value: string) => void-Handler called when all slots are filled
isDisabledbooleanfalseWhether the input is disabled
isInvalidbooleanfalseWhether the input is in an invalid state
patternstring-Regex pattern for allowed characters (e.g., REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS)
inputModeTextInputProps['inputMode']'numeric'Input mode for the input
placeholderstring-Placeholder text for the input. Each character corresponds to a slot position
placeholderTextColorstring-Placeholder text color for all slots
placeholderTextClassNamestring-Placeholder text class name for all slots
pasteTransformer(text: string) => string-Transform pasted text (e.g., remove hyphens). Defaults to removing non-matching characters
onFocus(e: FocusEvent) => void-Handler for focus events
onBlur(e: BlurEvent) => void-Handler for blur events
textInputPropsOmit<TextInputProps, ...>-Additional props to pass to the underlying TextInput component
childrenReact.ReactNode-Children elements to be rendered inside the InputOTP
classNamestring-Additional CSS classes to apply
stylePressableProps['style']-Style to pass to the container Pressable component
animation"disable-all" | undefinedundefinedAnimation configuration. Use "disable-all" to disable all animations including children

InputOTP.Group

proptypedefaultdescription
childrenReact.ReactNode | ((props: InputOTPGroupRenderProps) => React.ReactNode)-Children elements to be rendered inside the group, or a render function that receives slot data and other context values
classNamestring-Additional CSS classes to apply
...ViewPropsViewProps-All standard React Native View props are supported

InputOTPGroupRenderProps

proptypedescription
slotsSlotData[]Array of slot data for each position
maxLengthnumberMaximum length of the OTP
valuestringCurrent OTP value
isFocusedbooleanWhether the input is currently focused
isDisabledbooleanWhether the input is disabled
isInvalidbooleanWhether the input is in an invalid state

InputOTP.Slot

proptypedefaultdescription
indexnumber-Zero-based index of the slot (required). Must be between 0 and maxLength - 1
childrenReact.ReactNode-Custom slot content. If not provided, defaults to SlotPlaceholder, SlotValue, and SlotCaret
classNamestring-Additional CSS classes to apply
styleViewStyle-Additional styles to apply
...ViewPropsViewProps-All standard React Native View props are supported

InputOTP.SlotPlaceholder

proptypedefaultdescription
childrenstring-Text content to display (optional, defaults to slot.placeholderChar)
classNamestring-Additional CSS classes to apply
styleTextStyle-Additional styles to apply
...TextPropsTextProps-All standard React Native Text props are supported

InputOTP.SlotValue

proptypedefaultdescription
childrenstring-Text content to display (optional, defaults to slot.char)
classNamestring-Additional CSS classes to apply
animationInputOTPSlotValueAnimation-Animation configuration for SlotValue
...TextPropsTextProps-All standard React Native Text props are supported

InputOTPSlotValueAnimation

Animation configuration for InputOTP.SlotValue component. Can be:

  • false or "disabled": Disable all animations
  • true or undefined: Use default animations
  • object: Custom animation configuration
proptypedefaultdescription
state'disabled' | boolean-Disable animations while customizing properties
wrapper.enteringEntryOrExitLayoutTypeFadeIn.duration(250)Entering animation for wrapper
wrapper.exitingEntryOrExitLayoutTypeFadeOut.duration(100)Exiting animation for wrapper
text.enteringEntryOrExitLayoutTypeFlipInXDown.duration(250).easing(...)Entering animation for text
text.exitingEntryOrExitLayoutTypeFlipOutXDown.duration(250).easing(...)Exiting animation for text

InputOTP.SlotCaret

proptypedefaultdescription
classNamestring-Additional CSS classes to apply
styleViewStyle-Additional styles to apply
animationInputOTPSlotCaretAnimation-Animation configuration for SlotCaret
isAnimatedStyleActivebooleantrueWhether animated styles (react-native-reanimated) are active. When false, the animated style is removed and you can implement custom logic
pointerEvents'none' | 'auto' | ...'none'Pointer events configuration
...ViewPropsViewProps-All standard React Native View props are supported

InputOTPSlotCaretAnimation

Animation configuration for InputOTP.SlotCaret component. Can be:

  • false or "disabled": Disable all animations
  • true or undefined: Use default animations
  • object: Custom animation configuration
proptypedefaultdescription
state'disabled' | boolean-Disable animations while customizing properties
opacity.value[number, number][0, 1]Opacity values [min, max]
opacity.durationnumber500Animation duration in milliseconds
height.value[number, number][16, 18]Height values [min, max] in pixels
height.durationnumber500Animation duration in milliseconds

InputOTP.Separator

proptypedefaultdescription
classNamestring-Additional CSS classes to apply
...ViewPropsViewProps-All standard React Native View props are supported

Hooks

useInputOTP

Hook to access the InputOTP root context. Must be used within an InputOTP component.

const { value, maxLength, isFocused, isDisabled, isInvalid, slots } =
  useInputOTP();

useInputOTPSlot

Hook to access the InputOTP.Slot context. Must be used within an InputOTP.Slot component.

const { slot, isActive, isCaretVisible } = useInputOTPSlot();

On this page