Skip to content
v2.5.0
English
Русский
Documentation

PinField

PinField is a compound component for fixed-length codes such as OTPs and PINs.

Patterns

OTP Verification

When to use:

  • users enter a short verification code from SMS, email, or authenticator apps

Composition notes:

  • keep the length fixed
  • set autoComplete='one-time-code' on PinFieldInput so the browser can offer the code
  • place helper or error copy nearby

Trade-offs:

  • easy to scan and correct
  • more specialized than a standard text field
6-digit-otp.tsx · 40 lines
import { Button, Label, PinField, PinFieldInput, PinFieldSlot, Text } from '@vega-ui/react';
import { FormEvent, useId, useState } from 'react';

export default function Example6DigitOtp() {
  const id = useId();
  const [submitted, setSubmitted] = useState('');

  const onSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setSubmitted(String(new FormData(event.currentTarget).get('otp')));
  };

Grouped Legacy Code

When to use:

  • the code is visually grouped in the product or in the external source users copy from

Composition notes:

  • use PinFieldSeparator only for visual grouping
  • keep actual input length consistent

Trade-offs:

  • mirrors real-world code formatting
  • adds one more layout decision
grouped-code-with-separator.tsx · 22 lines
import { Label, PinField, PinFieldInput, PinFieldSeparator, PinFieldSlot } from '@vega-ui/react';
import { useId } from 'react';

export default function GroupedCodeWithSeparator() {
  const id = useId();

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-3)', width: '100%', maxWidth: 320 }}>
      <Label htmlFor={id}>Code from your authenticator app</Label>
      <PinField maxLength={6}>
        <PinFieldInput id={id} name='totp' autoComplete='one-time-code' />
        <PinFieldSlot index={0} />

Alphanumeric Activation Code

When to use:

  • the code accepts letters and digits

Composition notes:

  • pass a matching mask
  • normalize case in onChangeValue when the backend expects capitals
  • align the frontend mask with backend validation

Trade-offs:

  • flexible
  • easier to drift from backend expectations if the mask is guessed
alphanumeric-activation-code.tsx · 29 lines
import { Label, PinField, PinFieldInput, PinFieldSlot, Text } from '@vega-ui/react';
import { useId, useState } from 'react';

export default function AlphanumericActivationCode() {
  const id = useId();
  const [code, setCode] = useState('');

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-3)', width: '100%', maxWidth: 320 }}>
      <Label htmlFor={id}>Activation code from the box</Label>
      <PinField
        mask={/^[A-Za-z0-9]+$/}

Comparison

Quick Decision Rule

Use PinField for short fixed-length codes that benefit from segmented display. Use PasswordField for secret strings and TextField for general text.

PinField vs PasswordField

Use PinField when:

  • the value is an OTP or short verification code

Use PasswordField when:

  • the value is a longer secret string with reveal or hide behavior

PinField vs TextField

Use PinField when:

  • segmentation improves readability and entry

Use TextField when:

  • the field is ordinary free-form input

Choose This Component When

  • code length is fixed and short
  • segmented visual entry helps scanning and correction
  • paste and auto-advance behavior are part of the expected UX

Do Not Choose This Component When

  • the value is free-form
  • the field needs generic text editing rather than slot-based entry

Troubleshooting

Typing Does Not Update The Visible Slots

Symptom

The segmented UI renders, but typed characters do not appear in the slots.

Likely Cause

PinFieldInput is missing from the composition.

How To Verify

  • inspect the rendered composition
  • confirm PinFieldInput is present inside the root

Fix

Add PinFieldInput inside PinField.

Slot Order Feels Wrong Or Caret Movement Is Confusing

Symptom

Arrow keys or typing appear to target the wrong slot.

Likely Cause

Slot indexes are not rendered in ascending order.

How To Verify

  • inspect the slot sequence
  • compare it with maxLength

Fix

Render PinFieldSlot items in ascending index order and keep them aligned with the intended code length.

The Backend Rejects Codes That Look Valid In The UI

Symptom

Users can type the code, but submission fails due to format mismatch.

Likely Cause

The frontend mask does not match backend expectations.

How To Verify

  • compare the current mask with the accepted backend format

Fix

Update the mask and placeholders so they represent the real accepted code format.