@preline/pin-input
v4.2.0
Published
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
Readme
PIN Input
A PIN Input plugin that allows to fill in data for inputs.
Contents
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.
npm i @preline/pin-inputCSS
Use @source to register the plugin's JavaScript path for Tailwind CSS scanning, then @import the plugin's CSS files into your Tailwind CSS file.
@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:
<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.
<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.
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.
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.
<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 elementdata-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
autofocusclass 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:
<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)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
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
// 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
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
// 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.
<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.
<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.
<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.
