MenuChevron Down
PinField - Docs - Artefact

PinField

Forms
Smart auto-detect

Introduction

A segmented input for short fixed-length codes — one-time passwords, SMS/email verification codes, PINs. Each character gets its own box, with keyboard navigation, paste/autofill support, and the same label/helper text/error text/validator conventions as Field.

Usage

Check your email for the 6-digit code
import { PinField } from "../components/ui";

export default function MyPage() {
  return (
    <PinField
      label="Verification code"
      helperText="Check your email for the 6-digit code"
      count={6}
      otp
      blurOnComplete
      onValueComplete={(details) => console.log(details.valueAsString)}
    />
  );
}

Character format

format ("numeric" | "alphanumeric" | "alphabetic", default "numeric") restricts what a box accepts and picks the right mobile keyboard via inputMode. Pass pattern with a custom per-character regexp source to override it entirely — useful for coupon-style codes.

<PinField format="alphanumeric" count={8} placeholder="_" />

Autofill

otp marks the field as an SMS/email one-time code. Rather than putting autocomplete="one-time-code" on every box (which makes browsers and password managers show a fill affordance on all of them at once), only the box the user would fill next advertises it — and accepts the full code length, so a mobile keyboard's OTP suggestion or a full paste can land in one shot and get redistributed across the remaining boxes automatically. Every other box opts out of autofill and password-manager icon overlays.

Keyboard and pointer behaviour

  • Typing a valid character fills the current box and advances focus; an already-focused box's content is selected first (selectOnFocus, on by default), so typing always replaces rather than getting silently blocked.
  • Backspace on an empty box clears and steps back into the previous one.
  • Arrow Left/Right move focus between boxes; Arrow Up/Down are suppressed rather than doing nothing useful.
  • Pasting or an OS/password-manager autofill distributes characters across boxes starting from the one it landed on.
  • Tab and click/pointer focus stop at the first empty box — you can't jump ahead of a gap, only edit an already-filled box or continue where you left off.

Validation

Like Field, PinField accepts a validator that runs against the joined value and can return false (generic error) or a string (custom message). It only re-validates once every box is filled.

<PinField
  count={6}
  validator={(value) =>
    value !== "000000" || "That code isn't valid"
  }
  errorText="Enter the code we sent you"
/>

Forms: auto-submit and reset

form associates the field's hidden submit input with a <form id> elsewhere in the document (or omit it if the field already lives inside the form). autoSubmit calls form.requestSubmit() the moment every box is filled, after firing onAutoSubmit; pressing Enter in any box also attempts a submit, since a form with several sibling inputs suppresses the browser's own submit-on-Enter. Resetting the form (a native <button type="reset">, or form.reset()) clears the field back to empty.

<form id="verify-form">
  <PinField form="verify-form" name="code" count={6} otp autoSubmit />
  <button type="reset">Clear</button>
</form>

Props

PropTypeDefaultDescription
countnumber4Number of boxes.
valuestring[]-Controlled value, one entry per box (forces interactive mode).
defaultValuestring[]-Initial value, one entry per box (forces interactive mode).
format"numeric" | "alphanumeric" | "alphabetic""numeric"Character class accepted per box.
patternstring-Custom per-character regexp source, overrides format.
placeholderstring"○"Shown in each empty box.
maskboolean-Renders each box as type="password".
otpboolean-Targets autocomplete="one-time-code" autofill at the active box only.
blurOnCompleteboolean-Blurs the last box once every box is filled.
autoFocusboolean-Focuses the first box on mount.
selectOnFocusbooleantrueSelects a box's content on focus so typing replaces it.
disabledboolean-Disables every box.
readOnlyboolean-Prevents editing.
requiredboolean-Marks every box as required.
invalidboolean-Forces the invalid state, overriding validator.
namestring-Form field name for the hidden submit input.
formstring-Associates the hidden input (and autoSubmit/reset) with a <form id>.
autoSubmitboolean-Calls form.requestSubmit() once complete (forces interactive mode).
onAutoSubmit(valueAsString: string) => void-Fired right before an autoSubmit submission is attempted.
labelChild-The field's label.
helperTextChild-Helper text shown below the boxes.
errorTextChild-Error text shown when invalid; a validator string result overrides this.
validator(value: string) => boolean | string-Validates the joined value once complete (forces interactive mode).
onValueChange(details: { value: string[]; valueAsString: string }) => void-Fires on every change (forces interactive mode).
onValueComplete(details: { value: string[]; valueAsString: string }) => void-Fires once every box is filled.
onValueInvalid(details: { index: number; value: string }) => void-Fires when a typed/pasted character is rejected by format/pattern.
interactiveboolean-Forces or forbids hydration as an island.