npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

animated-otp-input

v0.1.3

Published

A fully customizable, animated OTP / verification code input for React Native

Readme

animated-otp-input

A fully animated, fully customizable OTP / verification-code input for React Native, built on react-native-reanimated.

  • 🎬 Per-cell fill animations — scale, bounce, fade, slide, flip, or none
  • 🌀 Completion-celebration animations played once the code is fully entered — orbit, ripple, ringSweep
  • 🫨 Built-in shake animation on error, plus an imperative ref.shake()
  • 🔒 Secure entry mode that briefly reveals a digit, then flips it into a mask dot
  • ✨ Blinking cursor and focus glow/pulse on the active cell
  • 📱 Native SMS/one-time-code autofill (textContentType="oneTimeCode" on iOS, autoComplete="sms-otp" on Android)
  • 🎨 Three built-in variants (boxed, underline, circle), a theme object, per-part style props, and a renderCell escape hatch to fully replace cell rendering
  • 🧩 Controlled or uncontrolled usage

Demo

Installation

npm install animated-otp-input react-native-reanimated

or

yarn add animated-otp-input react-native-reanimated

react-native-reanimated is a peer dependency (>=3.0.0). If you haven't set it up yet, follow the Reanimated installation guide — you need react-native-reanimated/plugin added as the last entry in your babel.config.js plugins array:

module.exports = {
  presets: ['babel-preset-expo'], // or your existing presets
  plugins: [
    // ...your other plugins
    'react-native-reanimated/plugin',
  ],
};

Quick start

import { useState } from 'react';
import { OtpInput } from 'animated-otp-input';

function VerifyScreen() {
  const [code, setCode] = useState('');

  return (
    <OtpInput
      length={6}
      value={code}
      onChange={setCode}
      onFilled={(value) => console.log('Complete code:', value)}
      autoFocus
    />
  );
}

Props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | length | number | 6 | Number of digits. | | value | string | — | Controlled value. | | defaultValue | string | '' | Initial value for uncontrolled usage. | | onChange | (value: string) => void | — | Fires on every change (typing, paste, autofill, backspace). | | onFilled | (value: string) => void | — | Fires once the value reaches length. | | autoFocus | boolean | false | Focus on mount. | | disabled | boolean | false | Disable input and dim cells. | | secureTextEntry | boolean | false | Reveal-then-mask each digit as it's typed. | | status | 'default' \| 'success' \| 'error' | 'default' | Drives color state; 'error' triggers the shake animation. | | variant | 'boxed' \| 'underline' \| 'circle' | 'boxed' | Built-in visual style. | | animationType | 'scale' \| 'fade' \| 'bounce' \| 'slide' \| 'flip' \| 'none' | 'scale' | Animation played when a cell fills. | | completionAnimationType | 'none' \| 'orbit' \| 'ripple' \| 'ringSweep' | 'none' | Celebratory animation played once when the value becomes fully filled — see Completion animations. | | completionAnimationDuration | number | 900 | Duration of the completion animation, in ms. | | shakeOnError | boolean | true | Whether status === 'error' triggers a shake. | | cursorAnimation | boolean | true | Blinking cursor on the focused empty cell. | | pulseOnFocus | boolean | true | Subtle scale/glow on the focused cell. | | allowedCharsRegex | RegExp | digits only (or alphanumeric if keyboardType="default") | Restrict which characters are accepted. | | gap | number | 8 | Space between cells. | | cellSize | number | 48 | Width/height of each cell. | | theme | OtpInputTheme | see below | Color tokens. | | containerStyle / cellStyle / focusedCellStyle / filledCellStyle / errorCellStyle / disabledCellStyle / textStyle | StyleProp | — | Per-part style overrides. | | renderCell | (props: OtpInputCellRenderProps) => ReactNode | — | Fully replace cell rendering. | | keyboardType | TextInputProps['keyboardType'] | 'number-pad' | Keyboard shown to the user. |

Imperative ref

import { useRef } from 'react';
import { OtpInput, type OtpInputRef } from 'animated-otp-input';

const ref = useRef<OtpInputRef>(null);

ref.current?.focus();
ref.current?.blur();
ref.current?.clear();
ref.current?.shake(); // trigger the shake animation manually
ref.current?.playCompletionAnimation(); // manually replay the completion animation

Completion animations

Set completionAnimationType to play a one-time celebration across the whole row when the value becomes fully filled (it also fires automatically — no need to call it yourself unless you want to replay it, e.g. after an async verification succeeds):

<OtpInput
  length={4}
  value={code}
  onChange={setCode}
  completionAnimationType="ripple"
/>
  • orbit — every cell loops through a small clockwise circle in unison, flashing an accent border together.
  • ripple — the same clockwise loop, staggered cell-by-cell into a left-to-right wave.
  • ringSweep — cells stay in place; a small dot sweeps clockwise around each cell's border, staggered in sequence across the row.

Theming

<OtpInput
  theme={{
    borderColor: '#D0D5DD',
    focusedBorderColor: '#4F46E5',
    filledBorderColor: '#9AA4B2',
    errorColor: '#E11D48',
    successColor: '#16A34A',
    backgroundColor: '#FFFFFF',
    focusedBackgroundColor: '#F5F4FF',
    textColor: '#101828',
    cursorColor: '#4F46E5',
  }}
/>

Fully custom cells

Use renderCell when the built-in variants and style props aren't enough:

<OtpInput
  length={4}
  value={value}
  onChange={setValue}
  renderCell={({ index, digit, isFocused, isFilled }) => (
    <View
      key={index}
      style={[
        styles.cell,
        isFocused && styles.cellFocused,
        isFilled && styles.cellFilled,
      ]}
    >
      <Text style={styles.cellText}>{digit}</Text>
    </View>
  )}
/>

Example app

See example/ for a full showcase covering every variant, animation type, secure entry, error/success states, and the renderCell escape hatch.

yarn
yarn example ios     # or android / web

Contributing

License

MIT


Made with create-react-native-library