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-smart-otp

v0.0.7

Published

A lightweight, accessible, and slot based fully customizable React OTP input component

Readme

React Smart Otp

Lightweight, accessible, and flexible React OTP (one-time passcode) input component with a slots API. Use it as-is for a simple, styled OTP row or supply custom slot components for deep UI-library integration (ShadCN, AntD, MUI, etc.).

Demo

Demo

If you found this project helpful, a ⭐ on GitHub would mean a lot — it helps others discover it too!

GitHub stars

Key features

  • Controlled API (pass value and onChange).
  • Slots-based customization: replace Container, Input, or Separator elements.
  • Keyboard-friendly: auto-advance on input, backspace moves back and clears.
  • Paste support: paste a full code into any input and it will populate the inputs (truncated to length).
  • Minimal CSS included (in src/component/react-otp/ReactOtp.css) and easy to theme.

Usage

Simple Example

import React, { useState } from "react"
import { ReactOtp } from "react-smart-otp"
// CSS required only when using default input slot and container
import "react-smart-otp/dist/index.css"

function Verify() {
  const [code, setCode] = useState("")

  return <ReactOtp length={6} value={code} onChange={setCode} />
}

Using Slots (Shadcn)

import { useState } from "react"
import { ReactOtp } from "react-smart-otp"
import { Button } from "./components/ui/button"
import { Card, CardContent } from "./components/ui/card"
import { Input } from "./components/ui/input"
import "react-smart-otp/dist/index.css"

function App() {
  const [otp, setOtp] = useState("")

  return (
    <>
      <ReactOtp
        value={otp}
        length={4}
        onChange={setOtp}
        defaultFocus={true}
        slots={{
          Container: ({ children }) => (
            <Card>
              <CardContent
                style={{
                  display: "flex",
                  justifyContent: "center",
                  flexDirection: "row",
                  alignItems: "center",
                  gap: "10px"
                }}
              >
                {children}
              </CardContent>
            </Card>
          ),
          Input: (props) => <Input className="w-10 text-center" {...props} />,
          Separator: (props) => <span {...props}>-</span>
        }}
      />
      <br />
      <Button onClick={() => setOtp("")}>Clear</Button>
    </>
  )
}

export default App

ReactOtp Component Props

| Prop | Type | Required | Default | Description | | ---------------- | --------------------------------------------------------------------------- | -------- | -------- | -------------------------------------------------------------- | | value | string | ✅ Yes | — | The full concatenated OTP value (controlled mode). | | length | number | ✅ Yes | — | Number of digits/inputs to render. | | onChange | (val: string) => void | ✅ Yes | — | Called with the concatenated value whenever any digit changes. | | inputType | 'text' \| 'password' \| 'number' | ❌ No | 'text' | Input type attribute applied to each input. | | defaultFocus | boolean | ❌ No | false | Focus the first input on mount when true. | | slots | { Container?: ElementType; Input?: ElementType; Separator?: ElementType } | ❌ No | — | Optional slot components to replace internal elements. |


Slots API

| Slot | Receives | Description | | ------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Container | HTMLAttributes<HTMLElement> | Used to wrap all inputs. | | Input | InputHTMLAttributes<HTMLInputElement> | Rendered for each digit. Component spreads ref, value, onChange, onKeyDown, onPaste, onFocus, maxLength, type, id, data-testid, and aria-label. | | Separator | HTMLAttributes<HTMLElement> | Rendered between each input, except after the last one. |


⚠️ Warning

When providing a custom Input slot, do not override the following props: ref, value, onChange, onFocus, onKeyDown, onPaste, type, maxLength, id, data-testid, or aria-label.

These are managed internally by the ReactOtp component. Overriding them may cause broken focus handling, incorrect value updates, or other unexpected behavior.

Behavioral Contract

  • The component is controlled — you must pass value and update it via onChange for the UI to reflect changes.
  • onChange receives the full concatenated string, e.g. "1234".

Styling / Theming

Minimal CSS is included in src/component/react-otp/ReactOtp.css — the primary class names are:

  • .otp-container — wrapper element.
  • .otp-input — default class applied to each input element.

You can style the component by:

  • Passing slot components that render library inputs or custom inputs.
  • Overriding or extending .otp-input in your app CSS.
  • Wrapping the component and applying utilities (Tailwind) to the custom Container slot.

Example CSS (already included):

.otp-container {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: center;
}
.otp-input {
  width: 40px;
  height: 40px;
  text-align: center;
  border-radius: 8px;
  border: 1px solid #bebebe;
}
.otp-input:focus {
  border-color: var(--otp-color, #007bff);
  outline: none;
}

Accessibility

  • Each input receives an aria-label in the format OTP input {index + 1} of {length} by default. You can replace inputs via slots and provide your own accessible labels if you prefer.
  • autoComplete="off" is set on inputs to avoid unwanted autofill. If you need browser or SMS autofill support, consider adding autoComplete="one-time-code" on the first input in a custom Input slot.

🤝 Contributing

We welcome contributions from the community!

You can contribute in two ways:

Feel free to open issues and pull requests!

License

License