Skip to main content

Pin Field

Pin Field is a form control that lets people enter a code in separate cells, such as a verification code or access code.

Import

import { PinField } from '@vortexlabs/vortex';
import type { PinFieldProps } from '@vortexlabs/vortex';

Other links

We sent a code to your email.
<PinField.Root
  size="sm"
  variant="outline"
>
  <PinField.Label>Verification code</PinField.Label>
<PinField.Input name="playground-code" />
<PinField.HelperText>We sent a code to your email.</PinField.HelperText>
<PinField.ErrorMessage>That code is incorrect.</PinField.ErrorMessage>
</PinField.Root>

Features

  • Supports numeric, alphabetic, and alphanumeric codes.
  • Supports configurable code lengths and responsive sizes.
  • Offers outline and fill variants with helper and error messages.
  • Accepts pasted codes and one-time-code autofill.
  • Supports controlled values, native form submission, and uncontrolled form reset.

Anatomy

<PinField.Root>
  <PinField.Label>Verification code</PinField.Label>
  <PinField.Input name="code" />
  <PinField.HelperText>We sent a code to your email.</PinField.HelperText>
  <PinField.ErrorMessage>That code is incorrect.</PinField.ErrorMessage>
</PinField.Root>
Component Part
Description
Links the label and messages to the input, and shares size and state.
Names the code input.
Displays one cell per character and submits the code under its name.
Adds supporting text below the cells.
Explains the error while the root error state is set.

Sizes

import { PinField, Stack } from "@vortexlabs/vortex";

<Stack gap={6}>
  <PinField.Root size="sm">
    <PinField.Label>Small</PinField.Label>
    <PinField.Input name="small-code" />
  </PinField.Root>
  <PinField.Root size="md">
    <PinField.Label>Medium</PinField.Label>
    <PinField.Input name="medium-code" />
  </PinField.Root>
  <PinField.Root size="lg">
    <PinField.Label>Large</PinField.Label>
    <PinField.Input name="large-code" />
  </PinField.Root>
</Stack>

Responsive Size

<PinField.Root size={{ xs: "sm", md: "lg" }} gap={{ xs: 2, md: 3 }}>
  <PinField.Label>Verification code</PinField.Label>
  <PinField.Input name="responsive-code" />
</PinField.Root>

Variants

<Stack gap={6}>
  <PinField.Root variant="outline">
    <PinField.Label>Outline</PinField.Label>
    <PinField.Input name="outline-code" />
  </PinField.Root>
  <PinField.Root variant="fill">
    <PinField.Label>Fill</PinField.Label>
    <PinField.Input name="fill-code" />
  </PinField.Root>
</Stack>

Length

Set length to the number of characters in the code. The default is 4.

<PinField.Root>
  <PinField.Label>Six-digit code</PinField.Label>
  <PinField.Input name="six-digit-code" length={6} />
</PinField.Root>

Character Patterns

Use pattern="alphabetic" for letters or pattern="alphanumeric" for letters and digits. The default is numeric.

<Stack gap={6}>
  <PinField.Root>
    <PinField.Label>Digits only</PinField.Label>
    <PinField.Input name="numeric-code" pattern="numeric" />
  </PinField.Root>
  <PinField.Root>
    <PinField.Label>Letters only</PinField.Label>
    <PinField.Input name="alphabetic-code" pattern="alphabetic" />
  </PinField.Root>
  <PinField.Root>
    <PinField.Label>Letters and digits</PinField.Label>
    <PinField.Input name="alphanumeric-code" pattern="alphanumeric" />
  </PinField.Root>
</Stack>

Disabled and Error States

Set error on PinField.Root to show PinField.ErrorMessage. Set disabled to prevent editing.

That code is incorrect.
<Stack gap={6}>
  <PinField.Root error>
    <PinField.Label>Verification code</PinField.Label>
    <PinField.Input name="error-code" defaultValue="12" />
    <PinField.ErrorMessage>That code is incorrect.</PinField.ErrorMessage>
  </PinField.Root>
  <PinField.Root disabled>
    <PinField.Label>Unavailable code</PinField.Label>
    <PinField.Input name="disabled-code" defaultValue="12" />
  </PinField.Root>
</Stack>

Controlled State

Set value and update it in onChange to control the code from your application.

Value: ""
"use client";

import { Button, PinField, Stack, Text } from "@vortexlabs/vortex";
import { useState } from "react";

export const PinFieldControlledExample = () => {
  const [value, setValue] = useState("");

  return (
    <Stack gap={3}>
      <Stack direction="row" gap={2}>
        <Button size="xs" variant="secondary" onClick={() => setValue("123456")}>
          Fill 123456
        </Button>
        <Button size="xs" variant="secondary" onClick={() => setValue("")}>
          Clear
        </Button>
      </Stack>
      <PinField.Root size="md">
        <PinField.Label>Verification code</PinField.Label>
        <PinField.Input name="controlled" length={6} value={value} onChange={({ value }) => setValue(value)} />
      </PinField.Root>
      <Text size="body-sm-desktop">Value: "{value}"</Text>
    </Stack>
  );
};

Initial Value and Form Reset

Set defaultValue for an uncontrolled field. A native form reset restores that value.

Reset restores the initial value 12.
import { Button, PinField, Stack } from "@vortexlabs/vortex";

<form>
  <Stack gap={3}>
    <PinField.Root size="md">
      <PinField.Label>Resettable code</PinField.Label>
      <PinField.Input name="resettable-code" length={6} defaultValue="12" />
      <PinField.HelperText>Reset restores the initial value 12.</PinField.HelperText>
    </PinField.Root>
    <Button type="reset" size="sm" variant="tertiary">Reset</Button>
  </Stack>
</form>

Accessibility

Key
Description
TabShiftTab
Move into or out of the field; the cells share one native input.
Left arrowRight arrow
Move between cells (directions swap in right-to-left layouts).
HomeUp arrow
Move to the first cell.
EndDown arrow
Move to the first empty cell, or the last cell when the code is full.
Include PinField.Label or provide attributes["aria-label"] on the input.
Helper and error messages are connected to the input with aria-describedby.
Validate the full code in your application; required checks for a non-empty value, not the configured length.