Propel

OTP Field

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

Basic

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

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

Magnitudes

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

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

const MAGNITUDES: OTPFieldInputMagnitude[] = ["md", "lg", "xl"];

export default function MagnitudesDemo() {
  return (
    <div className="flex flex-col items-start gap-4">
      {MAGNITUDES.map((magnitude) => (
        <OTPField
          key={magnitude}
          length={6}
          magnitude={magnitude}
          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} magnitude="md" groups={[3, 3]} 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}
      magnitude="md"
      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}
      magnitude="md"
      validationType="alphanumeric"
      normalizeValue={(value) => value.toUpperCase()}
      onValueChange={() => setError(null)}
      onValueInvalid={() => setError("Only letters and numbers are allowed")}
      error={error}
      aria-label="Recovery code"
    />
  );
}

Installation

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

Props

OTPField

PropTypeRequiredDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, OTPFieldRootState>NoAllows 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.
magnitude"md" | "lg" | "xl"YesBox size passed to every slot.
errorReactNodeNoError text shown below the slots; its presence flips every slot to the danger state.
groupsreadonly number[]NoSlot 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.