|Kit

Pin Input

A row of single-character cells for one-time codes and PINs on Ark UI's PinInput machine — autofocus advance, paste-to-fill, masking, OTP autofill, and native form submission.

PinInput is a row of single-character cells for one-time codes, PINs, and confirmation tokens, built on Ark UI's PinInput machine. Each cell is a real <input>; the machine handles autofocus advance, paste-to-fill across cells, backspace navigation, masking, and otp autofill, and a hidden input backs form submission.

import { PinInput } from '@42/ui-react/pin-input';

<PinInput length={6} otp />

The value is a single string — the machine speaks string[] (one char per cell), but value / defaultValue take a string and onChange / onComplete hand one back. Like the rest of the family it carries its own label, description, error, and required — no external wrapper needed — and passing error flips the cells to the invalid (red) state. Omit them and it still composes cleanly inside a Field.

Playground

Component

Loading preview

Props

Code

<Component  label="Verification code"  length={4}  size="md"  type="numeric"/>

One-time code

otp opts the cells into autocomplete="one-time-code", so iOS / Android surface the SMS code above the keyboard and fill every cell with one tap.

Masked

mask hides the entered characters like a password field — for PINs that shouldn't be shoulder-surfed.

Value type

type constrains what each cell accepts: numeric (default), alphanumeric, or alphabetic. Numeric cells also hint the numeric keyboard on mobile.

Sizes

Five sizes — xs through xl — matching the rest of the family. Each cell is a square that scales with the text.

Code

<PinInput size="xs" length={4} /><PinInput size="sm" length={4} /><PinInput size="md" length={4} /><PinInput size="lg" length={4} /><PinInput size="xl" length={4} />

Colors

Like the rest of the form family, the cells are palette-independent at rest — only the focus ring references --c-solid, so each palette tints the focused cell for free. See Colors.

Code

<PinInput color="brand" length={4} /><PinInput color="green" length={4} /><PinInput color="purple" length={4} />

Invalid & disabled

invalid swaps every cell's border and ring to red regardless of color (and sets aria-invalid). disabled dims and blocks the cells; readOnly keeps full contrast but rejects edits.

Code

<PinInput invalid defaultValue="12" length={4} /><PinInput disabled defaultValue="1234" length={4} /><PinInput readOnly defaultValue="1234" length={4} />

With label, description & error

label, description (helper text), and error are built in via Field — no external wrapper needed. Passing error flips the cells to the invalid (red) state; required adds the semantic flag and a red asterisk on the label.

Enter the 6-digit code from your authenticator app.
That code is incorrect

Code

<PinInput length={6} otp label="Verification code" description="Enter the 6-digit code from your authenticator app." /><PinInput length={6} required label="Verification code" /><PinInput length={6} label="Verification code" error="That code is incorrect" defaultValue="12" />

If you omit these props, the cells still compose inside an outer Field, inheriting its invalid, disabled, and readOnly from context — so an error on the field flips them red automatically.

Controlled

Drive the value from state. onChange fires on every keystroke with the joined string; onComplete fires once every cell is filled.

'use client';
import { useState } from 'react';
import { PinInput } from '@42/ui-react/pin-input';

export function CodeInput() {
  const [value, setValue] = useState('');
  return (
    <PinInput
      length={6}
      value={value}
      onChange={setValue}
      onComplete={(code) => verify(code)}
    />
  );
}

Forms

A name wires the hidden input into native <form> submission and FormData — the value submits as the joined string.

<form action={verify}>
  <PinInput name="code" length={6} />
</form>

API

Prop

Type

Accessibility

  • Full PinInput keyboard model from Ark: typing advances to the next cell, Backspace clears and steps back, / move between cells, and pasting a code fills every cell at once.
  • Each cell is labelled (Pin input 1 of N, …) and the cells are grouped, so assistive tech announces position and length.
  • invalid sets aria-invalid on the cells. Pass label / description / error for an accessible name and error text via the built-in Field, or compose inside an outer Field instead.
  • A hidden input backs form submission; pass name to include the joined value, and otp to enable OS-level one-time-code autofill.

On this page