OTP input

Free · MITReact Native · Expo

A verification code field where digits pop in, a pending check breathes, success ripples across the cells and a wrong code shakes and clears.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add https://goodui.dev/native/r/otp-input.json

Then run npx expo install --fix so react-native-reanimated match your Expo SDK. Files land in components/goodui-native/otp-input.

Usage

import { OtpInput } from "@/components/goodui-native/otp-input/otp-input";

<OtpInput autoFocus onComplete={(code) => api.verify(code)} />

When to use

Use it for
  • Sign in and sign up verification, two factor codes, email confirmation codes.
Reach for something else
  • Passwords or PINs that must stay hidden: use a secure text field.
  • Codes with letters: this field accepts digits only.

API reference

PropTypeDefault
length
Number of digits. Default 6.
number6
onComplete*
Called when every cell is filled. Return a promise: resolve to accept the code, reject to shake, clear and try again.
(code: string) => unknown—
onChange
Called on every change.
(code: string) => void—
autoFocus
Focus the field and open the keyboard on mount.
booleanfalse
errorText
Message under the cells after a rejected code. Same as `labels.error`.
string—
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<OtpInputLabels>—
accessibilityLabel
Overrides the label read by screen readers.
string—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • Digits land with a small pop on the bouncy spring; the active cell has a blinking caret and an accent border.
  • While a returned promise is pending the cells breathe in a cascade.
  • Success ripples a scale wave across the cells and tints them green; an error tints them red and shakes the row.
  • Reduced motion: no pop, wave, breathing or shake; colors still change.
  • Haptic: success when the code is accepted
  • Haptic: error when it is rejected
  • Each cell animates on the UI thread; typing re-renders only the cells.

Accessibility

  • One real TextInput owns the keyboard, paste, iOS one-time-code and Android SMS autofill, and the screen reader label ('Verification code, 6 digits').
  • The cells are hidden from screen readers. Results are announced.

Notes for AI

  • Return a promise from onComplete: resolve to accept, reject to shake and clear. Do not manage error state yourself.
  • Do not wrap it in another TextInput or add your own keyboard handling.