preline
Version:
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
314 lines (225 loc) • 10 kB
Markdown
# PIN Input
A PIN Input plugin that allows to fill in data for inputs.
[](https://www.npmjs.com/package/@preline/pin-input) [](https://opensource.org/licenses/MIT) [](https://preline.co/plugins/pin-input.html)
## Contents
- [Overview](#overview)
- [Installation](#installation)
- [Basic usage](#basic-usage)
- [Configuration Options](#configuration-options)
- [JavaScript API](#javascript-api)
- [Events](#events)
- [Common Patterns](#common-patterns)
- [License](#license)
## Overview
The PIN Input component provides a multi-field input interface for entering PIN codes, verification codes, or similar numeric/alphanumeric sequences. It automatically moves focus between fields and supports paste operations.
**Key Features:**
- Multiple input fields for PIN entry
- Automatic focus management
- Paste support for bulk entry
- Character validation via regex
- Programmatic control via JavaScript API
- Event system for completion tracking
- Accessibility support
## Installation
To get started, install PIN Input plugin via npm, else you can skip this step if you are already using Preline UI as a package.
```bash
npm i @preline/pin-input
```
### CSS
Use [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.
```css
@import "tailwindcss";
/* @preline/pin-input */
@source "../node_modules/@preline/pin-input/*.js";
@import "./node_modules/@preline/pin-input/variants.css";
@import "./node_modules/@preline/pin-input/theme.css";
```
### JavaScript
Include the JavaScript that powers the interactive elements near the end of your `</body>` tag:
```html
<script src="./node_modules/@preline/pin-input/index.js"></script>
```
### Manual Initialization
Use the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.
```html
<script type="module">
import HSPinInput from "@preline/pin-input/non-auto.mjs";
new HSPinInput(document.querySelector("#pin-input"));
</script>
```
### Via Bundler
When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.
`@preline/pin-input` is the auto-init entry: it scans the DOM and initializes matching elements automatically.
```js
import "@preline/pin-input";
```
`@preline/pin-input/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.
```js
import HSPinInput from "@preline/pin-input/non-auto";
HSPinInput.autoInit();
// Or initialize a specific element manually
const el = document.querySelector("#pin-input");
if (el) new HSPinInput(el);
```
### TypeScript
This package ships with TypeScript type definitions. No additional `@types/` package is needed.
## Basic usage
The following example demonstrates the minimal HTML structure required for a PIN input component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component consists of multiple input fields that automatically advance focus.
```html
<div data-hs-pin-input>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
</div>
```
**Structure Requirements:**
- `data-hs-pin-input`: Required on the container element
- `data-hs-pin-input-item`: Required on each input field
- All input fields should be direct children of the container
**Initial State:**
- Input fields are empty by default
- Focus starts on the first input field (if `autofocus` class is used)
## Configuration Options
### Data Options
Data options are specified in the `data-hs-pin-input` attribute.
| Attribute | Target Element | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `data-hs-pin-input` | Container | - | - | Activate a Pin Input by specifying on an element. Should be added to the container. |
| `:availableCharsRE` | Inside `data-hs-pin-input` | RegExp (string) | `"^[a-zA-Z0-9]+$"` | Regular expression string for available characters. Only characters matching this regex will be accepted. |
| `data-hs-pin-input-item` | Input element | - | - | Determines which element inside the initialized container will be responsible for entering data. Should be added to the input. |
**Example:**
```html
<div data-hs-pin-input='{
"availableCharsRE": "^[0-9]+$"
}'>
<!-- Input fields -->
</div>
```
### CSS Classes (Modifiers)
CSS class modifiers use Tailwind-style syntax with `--` prefix to control PIN input behavior.
| Class | Required On | Purpose |
| --- | --- | --- |
| `autofocus` | One of the input fields | If one of the fields has this class, it will be focused when the page loads. |
### Tailwind Modifiers
| Name | Description |
| --- | --- |
| `hs-pin-input-active:*` | The class is added to the input when the PIN has been set up (all fields are filled). |
## JavaScript API
The `HSPinInput` object is available in the global `window` object after the plugin is loaded.
### Instance Methods
These methods are called on a PIN input instance.
| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `destroy()` | None | `void` | Destroys the PIN input instance, removes all generated markup, classes, and event listeners. Use when removing PIN input from DOM. |
### Static Methods
These methods are called directly on the `HSPinInput` class.
| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `HSPinInput.getInstance(target, isInstance)` | `target`: `HTMLElement \| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSPinInput \| { id: string \| number, element: HSPinInput } \| null` | Returns the PIN input instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSPinInput` instance. If `isInstance` is `false` or omitted, returns the `HSPinInput` instance directly. Returns `null` if PIN input instance is not found. |
### Usage Examples
**Example 1: Destroying PIN input instance**
```javascript
const instance = HSPinInput.getInstance('#hs-pin-input', true);
if (instance) {
const { element } = instance;
const destroyBtn = document.querySelector('#hs-destroy-btn');
destroyBtn.addEventListener('click', () => {
element.destroy();
});
}
```
**Example 2: Getting instance and accessing properties**
```javascript
// Get the PIN input instance
const instance = HSPinInput.getInstance('#hs-pin-input', true);
if (instance) {
const { element } = instance;
// Access instance properties
console.log('Current value:', element.currentValue);
console.log('Items:', element.items);
// Clean up when removing from DOM
function removePinInput() {
element.destroy();
}
}
```
**Example 3: Destroying PIN input instance**
```javascript
const instance = HSPinInput.getInstance('#hs-pin-input', true);
if (instance) {
const { element } = instance;
const removeBtn = document.querySelector('#hs-remove-btn');
removeBtn.addEventListener('click', () => {
// Clean up before removing from DOM
element.destroy();
// Now safe to remove the element
document.querySelector('#hs-pin-input').remove();
});
}
```
## Events
PIN input instances emit events that can be listened to for completion tracking and custom behavior.
| Event Name | When Fired | Callback Parameter | Description |
| --- | --- | --- | --- |
| `on:completed` | When the PIN has been set up (all fields filled) | `{ currentValue: string }` | Fires when all input fields have been filled. Returns an object with the `currentValue` containing the complete PIN string. |
### Event Usage Example
```javascript
// Get PIN input instance
const instance = HSPinInput.getInstance('#hs-pin-input', true);
if (instance) {
const { element } = instance;
// Listen to completed event
element.on('completed', ({ currentValue }) => {
console.log('PIN completed:', currentValue);
// Perform actions after PIN is completed
// e.g., validate PIN, submit form, proceed to next step
});
}
```
## Common Patterns
### Pattern 1: Numeric PIN Only
Restrict input to numbers only.
```html
<div data-hs-pin-input='{
"availableCharsRE": "^[0-9]+$"
}'>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
</div>
```
### Pattern 2: Auto-focus First Field
Automatically focus the first field on page load.
```html
<div data-hs-pin-input>
<input type="text" data-hs-pin-input-item class="autofocus">
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
</div>
```
### Pattern 3: Handling Completion
Handle PIN completion with event listener.
```html
<div id="hs-pin-input-first" data-hs-pin-input>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
<input type="text" data-hs-pin-input-item>
</div>
<script>
const instance = HSPinInput.getInstance('#hs-pin-input-first', true);
if (instance) {
const { element } = instance;
element.on('completed', ({ currentValue }) => {
console.log('PIN entered:', currentValue);
// Validate or submit the PIN
});
}
</script>
```
## License
Copyright (c) 2026 Preline Labs.
Licensed under the [MIT License](https://opensource.org/licenses/MIT).