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

@xsolla/xui-input-pin

v0.199.0

Published

A cross-platform React PIN/OTP input component with individual cells for each digit. Features auto-advance between cells, paste support, and completion callbacks. <!-- BEGIN:xui-mcp-instructions:input-pin --> A specialised input for entering a short numer

Readme

Input Pin

A cross-platform React PIN/OTP input component with individual cells for each digit. Features auto-advance between cells, paste support, and completion callbacks.

A specialised input for entering a short numeric or alphanumeric code — typically a PIN, OTP (one-time password), or verification code. Renders as a horizontal row of individual character cells (.pin-item), one per expected digit. Each cell has independent state, and the component supports masking, placeholder dots, and a visibility toggle.

When to use

  • For entering a PIN, OTP, SMS verification code, or 2FA code
  • When a fixed-length code must be collected and each character should be visually distinct and finger-friendly
  • In authentication flows, payment confirmation screens, and account verification modals
  • When real-time digit-by-digit visual feedback improves the entry experience

When not to use

  • For variable-length passwords — use InputPassword
  • For long codes (more than 8 characters) that do not benefit from individual cell rendering — use a standard Input with masking
  • For alphanumeric codes where the user must type non-numeric characters (letters) that are already clearly visible — use a styled Input instead of a PIN field
  • When the code length is unknown ahead of time — InputPin requires a fixed number of cells

Behaviour guidelines

Auto-advance — after the user types a digit into the active cell, focus automatically moves to the next empty cell. The user should never need to manually click individual cells.

Auto-submit — when the last cell receives a value and all cells are filled (State=Filled), submit the form automatically without requiring the user to press Enter or a Submit button. This is the expected UX for OTP and PIN flows.

Backspace — pressing Backspace clears the value in the current active cell and moves focus back to the previous cell. If the active cell is already empty, Backspace moves focus back and clears the previous cell.

Paste — when the user pastes a string of the correct length, distribute the characters across all cells immediately and submit if auto-submit is enabled. Trim whitespace and hyphens from pasted values (e.g. 1234 5678 → 12345678).

Numeric-only enforcement — for numeric PINs/OTPs, reject non-numeric characters silently (do not show an error for a single wrong keypress — just ignore it). Use inputmode="numeric" to trigger a numeric keyboard on mobile.

Error state — switch to State=Error when the code fails server-side validation (wrong OTP, expired code, incorrect PIN). All cells switch to error appearance simultaneously. Show a specific error message below the component. Clear the error state when the user starts typing again and reset all cells.

OTP code expiry — for time-based OTPs, show a countdown timer or "Resend code" link near the component. If the code expires mid-entry, show State=Error with the message "Code has expired. Request a new one."

Resend flow — after an error due to expiry or incorrect code, provide a "Resend code" action that clears all cells, resets to State=Default, and moves focus to the first cell.

Disabled state — use State=Disable when the PIN entry is temporarily locked (e.g. too many failed attempts). Show a reason nearby (e.g. "Too many attempts. Try again in 30 seconds.").

Mobile keyboard — use inputmode="numeric" and pattern="[0-9]"* on the underlying input. Consider rendering a single hidden text input that captures keystrokes and distributes characters to the visual cells, rather than using multiple separate elements.

Content guidelines

Label — always provide a visible label above the component: "Enter PIN", "Verification code", "Enter the 6-digit code sent to +7 (900) •••• 1234". Include the code length and delivery channel in the label for OTP flows. Error messages — be specific and actionable:

  • "Incorrect PIN. 2 attempts remaining."
  • "Code expired. Request a new code below."
  • "Incorrect code. Please try again."
  • Resend link label — use "Resend code" or "Send a new code". Include a cooldown timer: "Resend code in 0:45".
  • Placeholder dot vs empty — prefer Placeholder-dots=True for PIN entry screens where the digit count helps the user count positions. Use Placeholder-dots=False for cleaner designs where the code length is communicated in the label.

Accessibility

The underlying input mechanism must be accessible via keyboard. The preferred implementation is a single visually-hidden element that captures keyboard input and distributes it to the visual cells — this avoids focus-management complexity across multiple inputs.

If multiple elements are used (one per cell), each must have aria-label identifying the position: e.g. aria-label="Digit 1 of 6".

The component container must have aria-label describing the whole field: e.g. aria-label="6-digit verification code".

When State=Error, the error message must be in an aria-live="polite" region and associated via aria-describedby so screen readers announce it.

The visual dot masking is cosmetic — the underlying must still use type="password" (or inputmode="numeric" on a text input with custom masking) to ensure the value is treated as sensitive.

Auto-advance (moving focus to the next cell) must also move focus for keyboard users, not just update visual state.

When the last cell is filled and the form is auto-submitted, announce the submission to screen readers via aria-live="assertive" or by focussing a confirmation message.

Ensure a minimum touch target of 44 × 44 px on mobile. For XS [32] and S [40] sizes, add layout padding to meet this requirement.

Installation

npm install @xsolla/xui-input-pin

Demo

Basic PIN Input

import * as React from "react";
import { InputPin } from "@xsolla/xui-input-pin";

export default function BasicPinInput() {
  const handleComplete = ({ value, isComplete }) => {
    if (isComplete) {
      console.log("PIN entered:", value);
    }
  };

  return <InputPin codeLength={4} onComplete={handleComplete} />;
}

Six-Digit Code

import * as React from "react";
import { InputPin } from "@xsolla/xui-input-pin";

export default function SixDigitPin() {
  return (
    <InputPin
      codeLength={6}
      label="Enter verification code"
      onComplete={({ value }) => console.log("Code:", value)}
    />
  );
}

Controlled PIN Input

import * as React from "react";
import { InputPin } from "@xsolla/xui-input-pin";

export default function ControlledPinInput() {
  const [pin, setPin] = React.useState("");

  return (
    <div>
      <InputPin
        value={pin}
        codeLength={4}
        onChange={({ value }) => setPin(value)}
      />
      <p>Current value: {pin}</p>
    </div>
  );
}

Secure Entry

import * as React from "react";
import { InputPin } from "@xsolla/xui-input-pin";

export default function SecurePinInput() {
  return (
    <InputPin codeLength={4} secureTextEntry={true} label="Enter your PIN" />
  );
}

Anatomy

import { InputPin } from "@xsolla/xui-input-pin";

<InputPin
  value={pinValue} // Controlled value
  onChange={handleChange} // Change handler with {value, isComplete}
  onComplete={handleComplete} // Called when all digits entered
  codeLength={4} // Number of digits
  size="md" // Size variant
  label="Label" // Label above input
  secureTextEntry={false} // Hide entered digits
  showPlaceholderDots={true} // Show dot placeholders
  flexibleWidth={false} // Expand cells to fill width
  disabled={false} // Disabled state
  error={false} // Error state
  errorMessage="Error" // Error message text
/>;

Examples

Full Width

import * as React from "react";
import { InputPin } from "@xsolla/xui-input-pin";

export default function FullWidthPin() {
  return (
    <div style={{ width: 300 }}>
      <InputPin codeLength={6} flexibleWidth={true} label="Verification Code" />
    </div>
  );
}

With Error

import * as React from "react";
import { InputPin } from "@xsolla/xui-input-pin";

export default function ErrorPinInput() {
  return (
    <InputPin
      codeLength={4}
      error={true}
      errorMessage="Invalid PIN. Please try again."
      label="Enter PIN"
    />
  );
}

PIN Input Sizes

import * as React from "react";
import { InputPin } from "@xsolla/xui-input-pin";

export default function PinInputSizes() {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
      <InputPin size="xs" codeLength={4} />
      <InputPin size="sm" codeLength={4} />
      <InputPin size="md" codeLength={4} />
      <InputPin size="lg" codeLength={4} />
      <InputPin size="xl" codeLength={4} />
    </div>
  );
}

API Reference

InputPin

InputPin Props:

| Prop | Type | Default | Description | | :------------------ | :----------------------------------------- | :------ | :------------------------------------------------------------------------------------------------------------ | | testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. | | value | string | "" | Current PIN value. | | onChange | (props: OnInputPinCompleteProps) => void | - | Called on every change. | | onComplete | (props: OnInputPinCompleteProps) => void | - | Called when all digits entered. | | codeLength | number | 4 | Number of PIN digits. | | size | "xl" \| "lg" \| "md" \| "sm" \| "xs" | "md" | Component size. | | label | string | - | Label above input. | | secureTextEntry | boolean | false | Hide entered digits. | | showPlaceholderDots | boolean | true | Show dot placeholders. | | flexibleWidth | boolean | false | Expand cells to fill container. | | disabled | boolean | false | Disabled state. | | error | boolean | false | Error state. | | errorMessage | string | - | Error message text. | | testID | string | - | Test identifier. | | aria-label | string | - | Accessible label for screen readers. |

OnInputPinCompleteProps:

interface OnInputPinCompleteProps {
  isComplete: boolean; // Whether all digits are filled
  value: string; // Current PIN value
}

Keyboard Navigation

| Key | Action | | :----------- | :------------------------------- | | 0-9, A-Z | Enter character and advance | | Backspace | Clear current cell and move back | | Arrow Left | Move to previous cell | | Arrow Right | Move to next cell | | Ctrl/Cmd + V | Paste and auto-fill cells |

Behavior

  • Auto-advances to next cell after input
  • Supports paste to fill multiple cells
  • Backspace clears current or moves to previous
  • Only alphanumeric characters are accepted
  • Uses inputMode="numeric" for mobile keyboards

Accessibility

  • Each cell has aria-label indicating digit position
  • role="group" on container with aria-labelledby
  • aria-invalid when in error state
  • Error messages linked via aria-describedby