Propel

OTP Field

A one-time-password field — a row of character slots that owns focus movement, paste, and completion.

Character 1
Show code
import { OTPField } from "@makeplane/propel/components/otp-field";

export default function BasicDemo() {
  return <OTPField length={6} size="lg" aria-label="Verification code" />;
}

Installation

import { OTPField } from "@makeplane/propel/components/otp-field";

Usage

import { OTPField } from "@makeplane/propel/components/otp-field";

export default function BasicDemo() {
  return <OTPField length={6} size="lg" aria-label="Verification code" />;
}

Examples

Sizes

size sets the box size of every slot — lg, xl, and 2xl.

Character 1
Character 1
Character 1
Show code
import { OTPField, type OTPFieldInputSize } from "@makeplane/propel/components/otp-field";

const SIZES: OTPFieldInputSize[] = ["lg", "xl", "2xl"];

export default function SizesDemo() {
  return (
    <div className="flex flex-col items-start gap-4">
      {SIZES.map((size) => (
        <OTPField
          key={size}
          length={6}
          size={size}
          defaultValue="123456"
          aria-label="Verification code"
        />
      ))}
    </div>
  );
}

Grouped

groups splits the run with separators — [3, 3] renders the 123-456 shape. The counts must sum to length.

Character 1
Show code
import { OTPField } from "@makeplane/propel/components/otp-field";

export default function GroupedDemo() {
  return <OTPField length={6} size="lg" groups={[3, 3]} aria-label="Verification code" />;
}

Hint

hint shows helper text below the slots, e.g. a resend countdown. It’s replaced by error when set.

Character 1

Resend OTP in 15 seconds

Show code
import { OTPField } from "@makeplane/propel/components/otp-field";

export default function HintDemo() {
  return (
    <OTPField length={6} size="lg" hint="Resend OTP in 15 seconds" aria-label="Verification code" />
  );
}

Invalid

Passing error shows the message and flips every slot to the danger state.

Character 1
Code is invalid
Show code
import { OTPField } from "@makeplane/propel/components/otp-field";

export default function InvalidDemo() {
  return (
    <OTPField
      length={6}
      size="lg"
      defaultValue="12"
      error="Code is invalid"
      aria-label="Verification code"
    />
  );
}

Controlled

normalizeValue rewrites accepted characters while rejected ones surface through onValueInvalid, fed back as an error until the next valid keystroke clears it.

Character 1
Show code
import { OTPField } from "@makeplane/propel/components/otp-field";
import * as React from "react";

export default function ControlledDemo() {
  const [error, setError] = React.useState<React.ReactNode>(null);
  return (
    <OTPField
      length={6}
      size="lg"
      validationType="alphanumeric"
      normalizeValue={(value) => value.toUpperCase()}
      onValueChange={() => setError(null)}
      onValueInvalid={() => setError("Only letters and numbers are allowed")}
      error={error}
      aria-label="Recovery code"
    />
  );
}

API Reference

OTPField

The ready-made one-time-password / verification-code field: a row of length character slots. Drive it with value/defaultValue + onValueChange; the root owns focus movement, paste, and completion across the slots. Pass mask to obscure entered characters, size to set the box size, hint for helper text below the slots (e.g. a resend countdown), and error to show an error message instead — Field.Root invalid then propagates data-invalid to every slot, which recolors its border (and focus ring) to danger off that state. Grafts Base UI OTPField behavior onto the elements/otp-field styled parts (OTPField root + OTPFieldInput). Each slot resolves its index from the root context, so the ready-made simply renders one per slot; groups splits the run with separators (e.g. groups={[3, 3]} renders 123-456).

PropTypeDefaultDescription
size(required)"lg" | "xl" | "2xl"—Box size passed to every slot.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, OTPFieldRootState>—Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render.
hintReactNode—Helper text shown below the slots, e.g. a resend countdown. Replaced by error when set.
errorReactNode—Error text shown below the slots; its presence flips every slot to the danger state.
groupsreadonly number[]—Slot counts per visual group, separated by an OTPFieldSeparator (e.g. [3, 3] renders 123-456). The counts must sum to length; omit for one flat run of slots.