Fluxo UIFluxo UIv0.4.93

PIN / OTP Input

A row of single-character fields that auto-advance — perfect for OTP verification, PIN entry, and short codes.

Basic Usage

6-digit code

Auto-advances to the next field as the user types. Paste a full code into any field to fill all six.

Value:
import { PinInput } from 'fluxo-ui';

const [code, setCode] = useState('');

<PinInput
    length={6}
    value={code}
    onChange={setCode}
    onComplete={(v) => verify(v)}
    autoFocus
/>

Variants

Box (default)

Bordered tiles — most familiar OTP look.

Underline

Only a bottom rule — minimal chrome for clean forms.

Soft

Filled background with no visible border — pairs well with light backgrounds.

<PinInput length={4} variant="box" />
<PinInput length={4} variant="underline" />
<PinInput length={4} variant="soft" />

Sizes

Small

Compact fields for inline forms.

Medium (default)

Balanced size for typical OTP screens.

Large

Bigger touch targets — ideal for sign-in screens on mobile.

<PinInput length={6} size="sm" />
<PinInput length={6} size="md" />
<PinInput length={6} size="lg" />

Advanced

Grouped digits

Insert a visual separator every N fields with groupAfter.

Alphanumeric + masked

type='alphanumeric' allows letters and digits; mask hides characters as dots.

Password with placeholder

Use type='password' for secret codes; add a placeholder character to indicate empty slots.

Error state

Mark the group invalid and add an errorText to announce the issue via role=alert.

Incorrect code — please try again
<PinInput length={6} groupAfter={3} type="numeric" />
<PinInput length={8} type="alphanumeric" mask />
<PinInput length={4} type="password" placeholder="•" />
<PinInput length={6} invalid errorText="Incorrect code" />

Import

import { PinInput } from 'fluxo-ui';
import type { PinInputProps, PinInputVariant, PinInputSize, PinInputType } from 'fluxo-ui';

Props

length
number"6"

Number of digit/character fields.

value
string

Controlled value of the combined code.

defaultValue
string"''"

Initial value when uncontrolled.

onChange
(value: string) => void

Called on every character change.

onComplete
(value: string) => void

Called when all fields are filled.

type
'numeric' | 'alphanumeric' | 'password'"'numeric'"

Allowed character set. 'password' also masks input.

mask
boolean"false"

Mask characters as dots/asterisks even if type isn't password.

variant
'box' | 'underline' | 'soft'"'box'"

Visual style. 'box' is a bordered tile, 'underline' uses a bottom border only, 'soft' uses a filled background.

size
'sm' | 'md' | 'lg'"'md'"

Visual size of each field.

disabled
boolean"false"

Disable all fields.

autoFocus
boolean"false"

Auto-focus the first field on mount.

placeholder
string"''"

Placeholder character for empty fields.

groupAfter
number

Insert a visual separator after every N fields (e.g., 3 for 3-3 grouping).

invalid
boolean"false"

Show error styling on the fields.

name
string

Name prefix used for each <input> (rendered as `${name}-${index}`).

className
string

Additional CSS class for the root.

ariaLabel
string"'Verification code'"

Accessible name for the group.

Features

Auto-advance

Typing a character moves focus to the next field; Backspace moves back.

Paste support

Pasting a full code distributes characters across remaining fields.

Three variants

box, underline, and soft — match any form aesthetic.

Numeric / alphanumeric / password

Input mode adapts so mobile keyboards show the right layout.

One-time-code autocomplete

autoComplete="one-time-code" enables iOS SMS auto-fill.

Invalid state

Error styling with role=alert announcement support.