suiss/uim · React Native Component

Input Otp

A row of single-character boxes for entering one-time codes, with autofill support.

Input Otp is an open-source mobile component from suiss/uim for React Native and Expo, with SwiftUI and Jetpack Compose versions built from the same design tokens. The @esuiss/uim CLI copies its source into your project, so you own the code.

iOS
Inputotp on iOS in light mode

InputOTP is backed by one hidden TextInput and shows each character in an InputOTPSlot. The active slot gets an accent border and a blinking caret. The hidden input uses the one-time-code autofill hints, so codes from messages can be filled in automatically, and pasting fills every slot.

Group slots with InputOTPGroup and place an InputOTPSeparator between groups for codes such as 123-456.

Installation

$ pnpm dlx @esuiss/uim@latest add input-otp

The CLI also adds what it builds on: Icon, Text.

Manual

  1. Install the dependencies:
npx expo install cn phosphor-react-native react-native-reanimated react-native-svg react-native-worklets
  1. Add the pieces it builds on: Icon, Text, and the primitives components/primitives/otp-field.tsx, components/primitives/use-render.tsx.

  2. Copy the source into components/ui/input-otp.tsx:

uniwind/components/ui/input-otp.tsx
import { cn } from "cn"
import { MinusIcon } from "phosphor-react-native"
import * as React from "react"
import { View } from "react-native"
import Animated, {
  cancelAnimation,
  ReduceMotion,
  useAnimatedStyle,
  useSharedValue,
  withDelay,
  withRepeat,
  withSequence,
  withTiming,
} from "react-native-reanimated"
import * as OTPFieldPrimitive from "@/components/primitives/otp-field"
import { resolveClassName } from "@/components/primitives/use-render"
  1. Update the import paths to match your project.

Usage

import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "@/components/ui/input-otp"
<InputOTP length={6}>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
    <InputOTPSlot index={2} />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot index={3} />
    <InputOTPSlot index={4} />
    <InputOTPSlot index={5} />
  </InputOTPGroup>
</InputOTP>

Set length and render one InputOTPSlot per index. Use value with onValueChange for controlled state, or defaultValue. onValueComplete fires when every slot is filled.

validationType limits input to numeric (the default number pad), alpha, alphanumeric or none, and onValueInvalid reports rejected characters. Set mask to hide the characters.

Use InputOTPSlotClassContext to style every slot at once, or className on a single slot.

Examples

Controlled

Controlled with value and onValueChange.

iOS
Inputotp (controlled) on iOS in light mode

Alphanumeric

Letters and numbers with validationType="alphanumeric".

iOS
Inputotp (alphanumeric) on iOS in light mode

Masked

A four-digit code with hidden characters.

iOS
Inputotp (masked) on iOS in light mode

Disabled

A disabled field with a partial value.

iOS
Inputotp (disabled) on iOS in light mode

API Reference

InputOTP

Root that owns the hidden input and the value.

ExtendsReact.ComponentProps<typeof OTPFieldPrimitive.Root>

InputOTPGroup

Row of adjacent slots.

ExtendsReact.ComponentProps<typeof View>

InputOTPSlot

Box that shows one character.

ExtendsReact.ComponentProps<typeof OTPFieldPrimitive.Input>

InputOTPSeparator

Dash between groups.

ExtendsReact.ComponentProps<typeof OTPFieldPrimitive.Separator>

Accessibility

  • Screen readers interact with the hidden input, labelled "N character code" by default; pass inputProps with an accessibilityLabel to localize it.
  • The visual slots are hidden from VoiceOver and TalkBack.
  • disabled is exposed on the input.

Notes

  • The hidden input sets textContentType="oneTimeCode" for iOS and autoComplete="one-time-code" for Android autofill.
  • The caret blink respects the system Reduce Motion setting.
  • Uses bg-control, border-accent and bg-accent tokens; light and dark follow the system appearance.