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

otp-input-kit

v1.0.35

Published

Highly customizable, framework-agnostic OTP input component with RTL, i18n, a11y, timer, and Web Component support

Readme

otp-input-kit

otp-input-kit preview

A highly customizable, framework-agnostic OTP input component with full RTL support, i18n, accessibility, countdown timer, toast notifications, and Web Component support — zero dependencies.

npm version license


Features

  • Zero dependencies — pure vanilla JS, ~30 KB minified
  • 10 built-in themes — default, underline, rounded, pill, ghost, filled, soft, neon, gradient, elevated
  • Full RTL & i18n — 12+ RTL locales, 8 numeral systems (Arabic-Indic, Persian, Hindi, Bengali, Tamil, Thai…)
  • WCAG 2.1 AA accessible — ARIA labels, live error regions, keyboard navigation, high-contrast & reduced-motion support
  • Countdown timer with progress bar and expiry callback
  • Resend button with configurable cooldown
  • Toast notifications — 6 themes, 9 positions, auto-dismiss
  • Clipboard paste detection with smart OTP extraction
  • Undo/Redo (Ctrl+Z / Ctrl+Shift+Z)
  • Haptic feedback (mobile vibration)
  • Web Component <otp-input> — drop in anywhere
  • ESM + UMD + CJS builds for every environment

Installation

npm

npm install otp-input-kit

CDN (no build step)

<script src="https://unpkg.com/otp-input-kit/dist/otp-input.umd.min.js"></script>

Quick Start

ES Module (bundler / Vite / Webpack)

import OTPInput from 'otp-input-kit';

const otp = OTPInput.create('#container', {
  length: 6,
  onComplete: (value) => console.log('OTP:', value),
});

UMD via <script> tag

<div id="container"></div>
<script src="https://unpkg.com/otp-input-kit/dist/otp-input.umd.min.js"></script>
<script>
  OTPInput.create('#container', {
    length: 6,
    onComplete: (value) => verifyOTP(value),
  });
</script>

Web Component

<script type="module">
  import 'otp-input-kit';
</script>

<otp-input length="6" theme="rounded" direction="rtl" locale="ar" native-numerals></otp-input>

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | length | number | 6 | Number of OTP digits | | type | 'numeric' \| 'alpha' \| 'alphanumeric' \| 'hex' \| 'custom' | 'numeric' | Allowed character type | | pattern | RegExp | null | Custom pattern (requires type: 'custom') | | secure | boolean | false | Mask input like a password field | | autoFocus | boolean | true | Focus first input on init | | autoSubmit | boolean | false | Submit parent <form> on completion | | selectOnFocus | boolean | true | Select digit text on focus | | direction | 'ltr' \| 'rtl' \| 'auto' | 'auto' | Input reading direction | | locale | string | null | BCP 47 locale tag (e.g. 'ar', 'fa', 'he') | | nativeNumerals | boolean | false | Render locale-specific digit glyphs | | placeholder | string | '·' | Empty cell placeholder character | | clipboardDetection | boolean | true | Detect and auto-fill pasted OTPs | | haptic | boolean | true | Vibration feedback on mobile | | theme | string | 'default' | Input theme (see Themes) | | validate | Function | null | (value) => errorString \| null | | animation | object | see below | Error animation config | | timer | object | see below | Countdown timer config | | resend | object | see below | Resend button config | | toast | object | see below | Toast notification config |

animation

animation: {
  error: 'shake',   // 'shake' | 'highlight' | 'both' | false
  duration: 300,    // ms
}

timer

timer: {
  enabled: true,
  duration: 60,          // seconds
  showProgress: true,    // animated progress bar
  onExpire: () => {},    // callback when time runs out
}

resend

resend: {
  enabled: true,
  cooldown: 60,                // seconds between resends
  label: 'Resend code',
  onResend: () => {},          // called when user clicks resend
}

toast

toast: {
  enabled: true,
  position: 'top-right',      // see positions below
  theme: 'default',           // see themes below
  duration: 3500,             // auto-dismiss delay in ms
  successMessage: 'Verified!',
  errorMessage: 'Invalid code',
}

Callbacks

OTPInput.create('#container', {
  onChange:   (value) => {},              // fires on every keystroke
  onComplete: (value) => {},             // fires when all digits filled
  onError:    (errors) => {},            // fires on validation failure
  onFocus:    ({ index, input }) => {},  // fires when a cell is focused
  onBlur:     ({ index, input }) => {},  // fires when a cell loses focus
  onExpire:   () => {},                  // fires when timer expires
  onResend:   () => {},                  // fires when resend is clicked
});

Methods

const otp = OTPInput.create('#container', options);

otp.getValue()           // → string of current digits
otp.setValue('123456')   // fill all cells programmatically
otp.clear()              // clear all cells
otp.focus()              // focus first empty cell
otp.setError('msg')      // show error state with message
otp.clearError()         // remove error state
otp.destroy()            // unmount and clean up DOM
otp.startTimer()         // start / restart countdown
otp.stopTimer()          // stop countdown

Events (EventEmitter API)

otp.on('complete', (value) => {});
otp.on('change',   (value) => {});
otp.on('error',    (errors) => {});
otp.on('focus',    ({ index }) => {});
otp.on('blur',     ({ index }) => {});
otp.on('expire',   () => {});
otp.on('resend',   () => {});

otp.off('complete', handler);
otp.once('complete', handler);

Web Component Attributes

<otp-input
  length="6"
  type="numeric"
  direction="rtl"
  locale="ar"
  native-numerals
  secure
  auto-focus
  auto-submit
  haptic
  placeholder="·"
  theme="rounded"
  timer-duration="60"
  resend-enabled
  resend-cooldown="60"
  clipboard-detection
  toast-enabled
  toast-theme="glass"
  toast-position="top-right"
  label="Enter verification code"
></otp-input>

Web Component Events

const el = document.querySelector('otp-input');
el.addEventListener('otp-complete', (e) => console.log(e.detail));
el.addEventListener('otp-change',   (e) => console.log(e.detail));
el.addEventListener('otp-error',    (e) => console.log(e.detail));
el.addEventListener('otp-expire',   () => {});
el.addEventListener('otp-resend',   () => {});

Themes

Pass via the theme option or the theme attribute on <otp-input>.

| Value | Preview description | |-------|-------------------| | default | Bordered boxes | | underline | Bottom border only | | rounded | Softly rounded corners | | pill | Fully rounded pill shape | | ghost | Transparent background | | filled | Solid fill background | | soft | Pastel/muted fill | | neon | Glowing accent border | | gradient | Gradient border | | elevated | Drop-shadow depth |

CSS Custom Properties

#my-container {
  --otp-input-width:    52px;
  --otp-input-height:   60px;
  --otp-font-size:      1.5rem;
  --otp-gap:            10px;
  --otp-radius:         10px;
  --otp-border-color:   #e2e8f0;
  --otp-active-color:   #3b82f6;
  --otp-error-color:    #ef4444;
  --otp-success-color:  #22c55e;
  --otp-bg:             #ffffff;
  --otp-text:           #0f172a;
}

RTL & Locale Examples

// Arabic — right-to-left with Eastern Arabic numerals
OTPInput.create('#ar', {
  length: 6, direction: 'rtl',
  locale: 'ar', nativeNumerals: true,
});

// Persian / Farsi
OTPInput.create('#fa', {
  length: 6, direction: 'rtl',
  locale: 'fa', nativeNumerals: true,
});

// Hebrew — RTL, Western numerals
OTPInput.create('#he', {
  length: 6, direction: 'rtl', locale: 'he',
});

Supported numeral systems: ar (Arabic-Indic ٠١٢٣٤٥٦٧٨٩), fa (Persian ۰۱۲۳۴۵۶۷۸۹), hi (Hindi ०१२३४५६७८९), bn (Bengali ০১২৩৪৫৬৭৮৯), ta (Tamil ௦௧௨௩௪௫௬௭௮௯), th (Thai ๐๑๒๓๔๕๖๗๘๙).


Validation

OTPInput.create('#container', {
  validate: (value) => {
    if (value === '000000') return 'This code is not allowed';
    if (!/^\d+$/.test(value)) return 'Numeric only';
    return null; // valid
  },
  onError: (errors) => console.error(errors),
});

Timer + Resend

OTPInput.create('#container', {
  length: 6,
  timer: {
    enabled: true,
    duration: 60,
    showProgress: true,
    onExpire: () => console.log('Code expired'),
  },
  resend: {
    enabled: true,
    cooldown: 60,
    label: 'Resend code',
    onResend: () => sendNewCode(),
  },
});

Package Formats

| Format | File | Use case | |--------|------|----------| | ESM | dist/otp-input.esm.js | Vite, Webpack, Rollup | | UMD | dist/otp-input.umd.js | <script> tag | | CJS | dist/otp-input.umd.cjs | Node.js require() | | Minified UMD | dist/otp-input.umd.min.js | CDN / production |


Browser Support

Chrome 80+, Firefox 75+, Safari 14+, Edge 80+. No IE11 support.


License

MIT © Fady Ehab