Pin Input
One-character-per-box input for PINs, OTPs, and verification codes with auto-advance, paste support, and optional masking.
<arc-pin-input> Overview
>
PinInput renders a row of individual input boxes — one per character — designed for entering PINs, one-time passwords, and verification codes. Each box accepts a single character and automatically advances focus to the next box on entry, creating a fast and fluid typing experience. The component supports backspace navigation (moving back to the previous box when the current one is empty), arrow key movement between boxes, and full clipboard paste that fills multiple boxes at once.
The `type` prop controls character validation: `"number"` restricts input to digits 0-9, `"alphanumeric"` allows letters and digits, and `"text"` accepts any single character. When `mask` is enabled, entered characters are obscured with dots (using CSS `-webkit-text-security: disc`) for sensitive codes. An optional `separator` prop inserts a visual dash between groups of boxes — for example, setting `separator="3"` on a 6-digit code renders it as three boxes, a dash, and three more boxes.
PinInput fires `arc-input` on every character entry or deletion, providing the current partial value. When all boxes are filled it fires `arc-change` — the commit for a fixed-length code — along with `arc-complete`, the more specific name kept for consumers that auto-submit. Either one makes it easy to trigger form submission or validation at the right moment without polling or length-checking.Guidelines
When to use
- Set `length` to match the expected code length — 4 for PINs, 6 for OTPs, etc.
- Use `type="number"` for numeric-only codes and set `inputmode="numeric"` for mobile keyboards
- Enable `mask` for sensitive codes like passwords or security PINs
- Listen for `arc-complete` to auto-submit or validate once the full code is entered
- Provide a `label` so users understand what code they are entering
When not to use
- Do not use PinInput for general text entry — it is designed exclusively for fixed-length codes
- Do not set `length` higher than ~8 — long codes are better handled with a standard text input
- Do not omit the `label` prop when the pin input is used standalone without surrounding context
- Do not use `separator` values that produce uneven groups at the end (e.g. `separator="4"` on a 6-digit code)
- Avoid placing PinInput in very narrow containers — each box needs at least 42px width plus gaps
Features
- Auto-advance focus to the next box after each valid character entry
- Backspace navigates to and clears the previous box when the current box is empty
- Arrow key navigation between boxes without modifying content
- Clipboard paste support that fills multiple boxes from the cursor position
- Configurable `type` validation: `"number"`, `"alphanumeric"`, or `"text"`
- Mask mode via `mask` prop for obscuring sensitive codes with dots
- Visual separator dashes between groups via the `separator` prop (e.g. every 3 boxes)
- Split events: `arc-input` on every keystroke, `arc-change` and `arc-complete` when all boxes are filled
Preview
Verification Code
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<arc-pin-input label="OTP Code" length="6" separator="3"></arc-pin-input>
<script>
document.querySelector('arc-pin-input')
.addEventListener('arc-complete', e => {
console.log('Code entered:', e.detail.value);
});
</script> import { PinInput } from '@arclux/arc-ui-react';
export default function Example() {
return (
<PinInput
label="OTP Code"
length={6}
separator={3}
onArcComplete={(e) => console.log('Code:', e.detail.value)}
/>
);
} <script setup>
import { PinInput } from '@arclux/arc-ui-vue';
</script>
<template>
<PinInput
label="OTP Code"
:length="6"
:separator="3"
@arc-complete="(e) => console.log('Code:', e.detail.value)"
/>
</template> <script>
import { PinInput } from '@arclux/arc-ui-svelte';
</script>
<PinInput
label="OTP Code"
length={6}
separator={3}
on:arc-complete={(e) => console.log('Code:', e.detail.value)}
/> import { Component } from '@angular/core';
import { PinInput } from '@arclux/arc-ui-angular';
@Component({
imports: [PinInput],
template: `
<arc-pin-input
label="OTP Code"
[length]="6"
[separator]="3"
(arc-complete)="onComplete($event)"
></arc-pin-input>
`,
})
export class MyComponent {
onComplete(e: CustomEvent) {
console.log('Code:', e.detail.value);
}
} import { PinInput } from '@arclux/arc-ui-solid';
export default function Example() {
return (
<PinInput
label="OTP Code"
length={6}
separator={3}
onArcComplete={(e) => console.log('Code:', e.detail.value)}
/>
);
} import { PinInput } from '@arclux/arc-ui-preact';
export default function Example() {
return (
<PinInput
label="OTP Code"
length={6}
separator={3}
onArcComplete={(e) => console.log('Code:', e.detail.value)}
/>
);
} API
-
size'sm' | 'md' | 'lg''md' - Control size. `md` is the default; `sm` and `lg` scale the digit boxes.
-
lengthnumber4 - Number of input boxes to render. Determines the expected code length.
-
valuestring'' - Current combined value across all boxes. Reflected as an attribute.
-
namestring'' -
disabledbooleanfalse - Disables all boxes, reducing opacity to 40% and blocking input.
-
maskbooleanfalse - When true, obscures entered characters with dots for sensitive codes.
-
type'number' | 'alphanumeric' | 'text''number' - Character validation mode. `number` allows digits only, `alphanumeric` allows letters and digits, `text` allows any character.
-
separatornumber0 - Inserts a visual dash separator every N boxes. Set to 0 to disable separators.
-
labelstring'' - Label text displayed above the input boxes in uppercase accent font.
-
readonlybooleanfalse - Prevents entering, deleting, or pasting characters while the boxes stay focusable and the value still submits.
-
formAssociatedbooleantrue -
propertiesobject{ required: { type: Boolean, reflect: true }, readonly: { type: Boolean, reflect: true }, } - Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. `required` participates in constraint validation below; `readonly` reflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state).
-
autoValidatesbooleantrue - Components that run their own constraint-validation logic (pattern checks, range checks) opt out of the automatic required sync by overriding this to false, and own the whole validity flag set instead.
-
form -
validity -
validationMessage -
requiredbooleanfalse
Events
-
arc-input - Fired on every character entry or deletion. `event.detail.value` contains the current partial value.
-
arc-change - Fired when the pin is complete — every box filled. That is the commit for a fixed-length value.
-
arc-complete - Fired alongside arc-change when all boxes are filled. The more specific name, kept for consumers that auto-submit.
See Also
- OTP Input A one-time password input that renders a row of individual character boxes with auto-advance, paste support, and configurable length and input type.
- Input Versatile form control supporting single-line text, email, password, and multiline textarea modes with built-in label, placeholder, and validation states. Pairs with Form for complete data-entry workflows.