GitHub

Input OTP

A one-time password input component for verification codes, ready for CMS integration.

CMSPayload CMS Integration

OTP inputs can be configured through CMS fields, allowing content editors to customize labels, number of digits, and validation messages without code changes.

CMS Demo

See how content editors configure OTP inputs in a CMS:

Loading...
CMS ConfigurationSimulates Payload CMS OTP fields with localization

Payload Form Example

// In your Payload CMS form config
export const VerificationForm = {
  slug: "verification-form",
  fields: [
    {
      name: "otpField",
      type: "group",
      fields: [
        { name: "label", type: "text", localized: true },
        { name: "length", type: "number", defaultValue: 6 },
        { name: "helpText", type: "text", localized: true },
      ],
    },
  ],
}

Rendering the Form

import { InputOTP, InputOTPGroup, InputOTPSlot } from "@mordecai-design-system/ui"

export function VerificationForm({ data }) {
  const field = data.otpField
  return (
    <div className="space-y-2">
      <label className="text-sm font-medium">{field.label}</label>
      <InputOTP maxLength={field.length}>
        <InputOTPGroup>
          {Array.from({ length: field.length }).map((_, i) => (
            <InputOTPSlot key={i} index={i} />
          ))}
        </InputOTPGroup>
      </InputOTP>
      <p className="text-xs text-muted-foreground">{field.helpText}</p>
    </div>
  )
}

Basic Usage

Loading...
Input OTPA 6-digit OTP input

Installation

pnpm add @corew500/ui

Usage

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
  InputOTPSeparator,
} from "@mordecai-design-system/ui"
<InputOTP maxLength={6}>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
    <InputOTPSlot index={2} />
    <InputOTPSlot index={3} />
    <InputOTPSlot index={4} />
    <InputOTPSlot index={5} />
  </InputOTPGroup>
</InputOTP>

Examples

With Separator

<InputOTP maxLength={6}>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
    <InputOTPSlot index={2} />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot index={3} />
    <InputOTPSlot index={4} />
    <InputOTPSlot index={5} />
  </InputOTPGroup>
</InputOTP>

Controlled

const [value, setValue] = useState("")

<InputOTP maxLength={6} value={value} onChange={setValue}>
  ...
</InputOTP>

Pattern Validation

// Only allow numbers
<InputOTP maxLength={6} pattern="[0-9]*">
  ...
</InputOTP>

Props

InputOTP

PropTypeDefaultDescription
valuestring——
onChange(newValue: string) => unknown——
maxLength*number——
textAlignenum——
onComplete(...args: any[]) => unknown——
pushPasswordManagerStrategyenum——
pasteTransformer(pasted: string) => string——
containerClassNamestring—Additional class name for the container element
noScriptCSSFallbackstring——
renderInputOTPRenderFn——

InputOTPSlot

PropTypeDefaultDescription
index*number—Zero-based index of this slot within the OTP input