Getting StartedComponentsDesign TokensThemingTheme SynthesizerFrameworksAccessibilityUtilitiesServer RenderingBrowser SupportContributingChangelog App ShellAspect GridAuth ShellCenterClusterContainerDashboard GridDockFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbar Anchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkSpeed DialStepper NavTabsTop BarTree View AccordionAspect RatioAvatarAvatar GroupCalloutCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSeparatorSkeletonSpinnerStackVideoVirtual List Activity HeatmapAnimated NumberBadgeChartClockComparisonCountdown TimerData GridData TableDescription ListDiffEvent CalendarGaugeJSON TreeKanbanKey ValueLevel MeterListMeterSparklineStatStepperTableTagTimelineUptimeValue CardWaveform BlockquoteCode BlockGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriter ButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputOTP InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree Select AlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuGuided TourHover CardInline MessageLoading OverlayModalNotification PanelPopoverProgressProgress ToastSheetSnackbarSpotlightToastTooltip
ARC UI ARC Radiant Components
v3.2 Docs Components Tokens Synthesizer
Getting StartedFrameworksServer Rendering Design TokensThemingTheme SynthesizerTypographyUtilities All ComponentsAccessibilityBrowser SupportChangelogContributingStats App ShellAspect GridAuth ShellCenterClusterContainerDashboard GridDockFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbar Anchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkSpeed DialStepper NavTabsTop BarTree View AccordionAspect RatioAvatarAvatar GroupCalloutCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSeparatorSkeletonSpinnerStackVideoVirtual List Activity HeatmapAnimated NumberBadgeChartClockComparisonCountdown TimerData GridData TableDescription ListDiffEvent CalendarGaugeJSON TreeKanbanKey ValueLevel MeterListMeterSparklineStatStepperTableTagTimelineUptimeValue CardWaveform BlockquoteCode BlockGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriter ButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputOTP InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree Select AlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuGuided TourHover CardInline MessageLoading OverlayModalNotification PanelPopoverProgressProgress ToastSheetSnackbarSpotlightToastTooltip

Pin Input

One-character-per-box input for PINs, OTPs, and verification codes with auto-advance, paste support, and optional masking.

Components Pin Input
input interactive
<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

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.
length number 4
Number of input boxes to render. Determines the expected code length.
value string ''
Current combined value across all boxes. Reflected as an attribute.
name string ''
disabled boolean false
Disables all boxes, reducing opacity to 40% and blocking input.
mask boolean false
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.
separator number 0
Inserts a visual dash separator every N boxes. Set to 0 to disable separators.
label string ''
Label text displayed above the input boxes in uppercase accent font.
readonly boolean false
Prevents entering, deleting, or pasting characters while the boxes stay focusable and the value still submits.
formAssociated boolean true
properties object { 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).
autoValidates boolean true
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
required boolean false

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