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

react-otp-package

v1.0.2

Published

A lightweight, dynamic, fully-accessible OTP input component for React. Supports any length, paste, keyboard navigation, error shake, resend timer and full customisation.

Readme

react-otp-package

A lightweight, dynamic, and fully-accessible OTP input component for React.

npm version bundle size license


✨ Features

  • 🔢 Any length — 4, 6, 8 digits, or whatever your backend needs
  • ⌨️ Smart keyboard navigation — Arrow keys, Backspace, and Tab all work naturally
  • 📋 Paste support — paste a full OTP and every field fills instantly
  • Accessiblerole="group", aria-label per input, inputMode="numeric" for mobile keyboards
  • 📱 Mobile-friendly — numeric/text keyboard pops up automatically
  • 🎨 Fully customisable — pass your own className or inline style for inputs and the container
  • Error state + shake animation — one prop drives both the red border and the shake
  • ⏱️ Built-in resend timer — opt-in countdown with a callback; no extra state to manage
  • 🔤 Alpha-numeric mode — accept letters too for PIN-style codes
  • 🪶 Tiny — ~2 KB gzipped, zero runtime dependencies beyond React

📦 Installation

npm install react-otp-package

Requires **React ≥ 17**.

---

## 🚀 Quick Start

```jsx
import { useState } from "react";
import OtpInput from "react-otp-package";

export default function VerifyPage() {
  const [otp, setOtp] = useState("");

  return (
    <OtpInput
      length={6}
      onChange={setOtp}
      onComplete={(code) => console.log("Complete OTP:", code)}
    />
  );
}

📖 Examples

Basic 6-digit OTP

<OtpInput length={6} onComplete={(code) => verifyOtp(code)} />

Controlled component

const [otp, setOtp] = useState("");

<OtpInput length={6} value={otp} onChange={setOtp} />;

Show error with shake animation

const [hasError, setHasError] = useState(false);

<OtpInput
  length={6}
  hasError={hasError}
  onComplete={(code) => {
    if (code !== correctOtp) setHasError(true);
  }}
/>;
{
  hasError && <p style={{ color: "red" }}>Incorrect OTP. Try again.</p>;
}

With built-in resend timer

<OtpInput
  length={6}
  showResend
  resendTimeout={30}
  onResend={() => {
    // call your API to re-send the code
    sendOtpToUser();
  }}
  resendLabel="Resend code"
/>

Custom styling

<OtpInput
  length={4}
  inputStyle={{
    width: "56px",
    height: "56px",
    fontSize: "1.75rem",
    borderRadius: "12px",
    border: "2px solid #7c3aed",
  }}
  containerStyle={{ gap: "12px" }}
/>

With separator between inputs

<OtpInput
  length={6}
  showSeparator
  separator={<span style={{ color: "#999" }}>·</span>}
/>

Alpha-numeric PIN (e.g. gift card codes)

<OtpInput
  length={8}
  isAlphaNumeric
  placeholder="·"
  onComplete={(code) => redeemCode(code)}
/>

Full real-world example

import { useState } from "react";
import OtpInput from "react-otp-package";

export default function VerifyPage() {
  const [otp, setOtp] = useState("");
  const [status, setStatus] = useState(null); // "success" | "error" | null

  async function handleComplete(code) {
    const ok = await verifyWithServer(code);
    setStatus(ok ? "success" : "error");
  }

  async function handleResend() {
    await requestNewOtp();
    setOtp("");
    setStatus(null);
  }

  return (
    <div style={{ textAlign: "center" }}>
      <h2>Enter your 6-digit code</h2>

      <OtpInput
        length={6}
        value={otp}
        onChange={setOtp}
        onComplete={handleComplete}
        hasError={status === "error"}
        showResend
        resendTimeout={60}
        onResend={handleResend}
        resendLabel="Resend OTP"
      />

      {status === "success" && <p style={{ color: "green" }}>✅ Verified!</p>}
      {status === "error" && (
        <p style={{ color: "red" }}>❌ Wrong code, try again.</p>
      )}
    </div>
  );
}

⚙️ Props

| Prop | Type | Default | Description | | -------------------- | ----------------------- | -------------- | ----------------------------------------------------- | | length | number | 6 | Number of OTP input boxes | | value | string | — | Controlled value. Use together with onChange | | onChange | (otp: string) => void | — | Called on every keystroke with the current OTP string | | onComplete | (otp: string) => void | — | Called when all fields are filled | | autoFocus | boolean | true | Auto-focus the first box on mount | | disabled | boolean | false | Disable all inputs | | isAlphaNumeric | boolean | false | Allow letters as well as digits | | placeholder | string | '' | Placeholder character for each empty box | | inputClassName | string | '' | Extra CSS class(es) on every input | | inputStyle | object | {} | Inline styles for every input | | containerClassName | string | '' | Extra CSS class(es) on the wrapper div | | containerStyle | object | {} | Inline styles for the wrapper div | | showSeparator | boolean | false | Render a separator between inputs | | separator | ReactNode | | Custom separator element | | shakeOnError | boolean | true | Animate a shake when hasError is true | | hasError | boolean | false | Marks inputs with a red border and triggers shake | | showResend | boolean | false | Render built-in Resend button with countdown | | resendTimeout | number | 30 | Countdown in seconds before Resend becomes active | | onResend | () => void | — | Called when the user clicks Resend | | resendLabel | string | 'Resend OTP' | Label for the Resend button / countdown text |


🎹 Keyboard Support

| Key | Action | | -------------- | ------------------------------------------------------- | | 0–9 / a–z | Enter the character and move focus to the next field | | Backspace | Clear current field; if empty, clear and focus previous | | ArrowRight | Move focus to next field | | ArrowLeft | Move focus to previous field | | Ctrl/Cmd + V | Paste: fills all fields from clipboard text |


🤝 Contributing

Contributions, issues, and feature requests are welcome!

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Commit your changes: git commit -m 'feat: add my feature'
  4. Push to the branch: git push origin feat/my-feature
  5. Open a Pull Request

📄 License

Yogesh Longwani