Components

OTPField

One-time password input coordination across multiple visible cells and one hidden form value.

When to Use

Use OTPField for a short verification code that is entered one character at a time and may be pasted from a message. Use PasswordToggleField for a reusable secret chosen by the person, and Input when the value belongs in one normal text box instead of visually separated cells.

Features

  • Controlled and uncontrolled full value.
  • Coordinates rendered input cells and assigns indexes from their render order.
  • Optional explicit input indexes.
  • Roving tab stop so the field behaves as one logical control.
  • Paste distribution across cells.
  • Arrow, Backspace, Delete, Home, and End navigation.
  • Numeric, alphabetic, alphanumeric, or custom pattern filtering.
  • Hidden input for native form submission.
  • Optional masking, completion callback, auto-focus, and form submission.

Import

import { OTPField } from "@flowstack-ui/atom";

Anatomy

<OTPField.Root>
  <OTPField.Input />
  <OTPField.Separator />
  <OTPField.Input />
</OTPField.Root>

API Reference

Root

Owns the complete code value, filtering, cell registration, focus movement, Field state, completion behavior, and optional hidden form input.

PropTypeDefault
childrenReactNoderequired
valuestring-
defaultValuestring""
onValueChange(value: string) => void-
onComplete(value: string) => void-
lengthnumber6
type"numeric" | "alphabetic" | "alphanumeric""numeric"
patternRegExpDerived from type
maskboolean | stringfalse
namestring-
formstring-
inputIdstringGenerated or inherited from Field
autoFocusbooleanfalse
autoSubmitbooleanfalse
disabledbooleanfalse
readOnlybooleanfalse
requiredbooleanfalse
invalidbooleanfalse
ariaLabelstring"Verification code"
ariaDescribedBystringField description IDs
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-labelValue from ariaLabel, or "Verification code" without an external label
aria-labelledbyInherited Field label ID when no direct label is provided
aria-describedbyValue from ariaDescribedBy or Field descriptions
aria-invalidPresent when invalid
aria-requiredPresent when required
Data attributeValues
[data-slot]"otp-field"
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-required]Present when required
[data-invalid]Present when invalid

Input

Renders one visible character cell, joins the roving tab stop, and delegates typing, paste, deletion, and focus movement to Root.

PropTypeDefault
childrenReactNode-
indexnumberDOM order
aria-labelstringGenerated from index and length
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-label"Digit N of length" or "Character N of length" by default
aria-invalidPresent when the root is invalid
aria-requiredPresent when the root is required
Data attributeValues
[data-slot]"otp-field-input"
[data-index]Zero-based cell index
[data-filled]Present when the cell has a value
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-invalid]Present when invalid

Separator

Decorative separator between cells.

PropTypeDefault
childrenReactNode-
indexnumber-
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-hiddentrue
Data attributeValues
[data-slot]"otp-field-separator"
[data-index]Value from index when provided

The package exports useOTPFieldContext and its provider for advanced compound parts. It also exports getOTPFieldPattern, isOTPFieldCharAccepted, getOTPFieldChars, filterOTPFieldValue, and getOTPFieldDisplayChar for using the same filtering and display rules outside the rendered field.

Examples

Six Digit Code

import { OTPField } from "@flowstack-ui/atom";

export function VerificationCode() {
  return (
    <OTPField.Root name="code" length={6}>
      {Array.from({ length: 6 }, (_, index) => (
        <OTPField.Input key={index} />
      ))}
    </OTPField.Root>
  );
}

Grouped Code

import { OTPField } from "@flowstack-ui/atom";

export function GroupedCode() {
  return (
    <OTPField.Root length={6} onComplete={(code) => console.log(code)}>
      <OTPField.Input />
      <OTPField.Input />
      <OTPField.Input />
      <OTPField.Separator>-</OTPField.Separator>
      <OTPField.Input />
      <OTPField.Input />
      <OTPField.Input />
    </OTPField.Root>
  );
}

Accessibility

The root uses role="group" and the visible inputs use roving tabIndex, so Tab enters the OTP field once. Each input receives a generated position label, and the separator is hidden from assistive technology. Give the group a clear label through ariaLabel, native labeling, or Field.

KeyDescription
TabEnters or leaves the OTP field as one logical control.
ArrowRightMoves to the next cell.
ArrowLeftMoves to the previous cell.
HomeMoves to the first cell.
EndMoves to the last cell.
BackspaceClears the current cell or moves backward when empty.
DeleteClears the current cell.
PasteDistributes accepted characters across cells.

Changelog

See CHANGELOG.md.