Gecko UIGecko UI

RHFOTPInput

OTP (One-Time Password) input component integrated with React Hook Form

RHFOTPInput

An OTP (One-Time Password) input component integrated with React Hook Form that provides a segmented input field for entering verification codes. Each digit is displayed in a separate box with automatic focus management and keyboard navigation. Automatically displays error states with a red border when validation fails.

Installation

import { RHFOTPInput } from '@geckoui/geckoui';

Basic Usage

import { useForm, FormProvider } from 'react-hook-form';
import { RHFOTPInput } from '@geckoui/geckoui';

function Example() {
  const methods = useForm({
    defaultValues: {
      basicOtp: ''
    }
  });

  return (
    <FormProvider {...methods}>
      <RHFOTPInput name="basicOtp" />
    </FormProvider>
  );
}

Props API

Extends all props from base OTPInput component plus:

PropTypeDefaultDescription
namestring-Field name (required)
controlControlAuto-injectedOptional: Pass explicitly for nested forms or custom form context
rulesRegisterOptions-Inline validation rules
lengthnumber6Number of OTP input fields to display
numberOnlybooleantrueIf true, only numeric input is allowed
onOTPComplete(value: string) => void-Callback when all OTP fields are filled
classNamestring-Wrapper div classname
inputClassNamestring-Individual input field classname
aspectRatiostring | number0.94Aspect ratio for each input box
disabledbooleanfalseDisable all input fields

Examples

With Validation (Zod)

import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFOTPInput, RHFInputGroup } from '@geckoui/geckoui';

const schema = z.object({
  validationCode: z.string().length(6, 'OTP must be exactly 6 digits').regex(/^\d+$/, 'OTP must contain only numbers')
});

function Example() {
  const methods = useForm({
    resolver: zodResolver(schema),
    mode: 'onBlur',
    defaultValues: { validationCode: '' }
  });

  return (
    <FormProvider {...methods}>
      <RHFInputGroup label="Verification Code" required>
        <RHFOTPInput name="validationCode" />
      </RHFInputGroup>
    </FormProvider>
  );
}

Six Digit Code

<RHFOTPInput name="sixDigitPin" length={6} />

Four Digit PIN

<RHFOTPInput name="fourDigitPin" length={4} />

Alphanumeric Code

const schema = z.object({
  activationCode: z.string().length(8, 'Activation code must be exactly 8 characters')
});

<RHFInputGroup label="Activation Code" required>
  <RHFOTPInput name="activationCode" length={8} numberOnly={false} />
</RHFInputGroup>

Disabled State

<RHFOTPInput name="disabledOtp" disabled />

Complete Form Example

import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFOTPInput, RHFInputGroup, Button } from '@geckoui/geckoui';

const schema = z.object({
  twoFactorCode: z.string().length(6, 'Code must be exactly 6 digits').regex(/^\d+$/, 'Code must contain only numbers')
});

function TwoFactorForm() {
  const methods = useForm({
    resolver: zodResolver(schema),
    mode: 'onBlur',
    defaultValues: {
      twoFactorCode: ''
    }
  });

  return (
    <FormProvider {...methods}>
      <form onSubmit={methods.handleSubmit(console.log)}>
        <RHFInputGroup label="Two-Factor Authentication Code" required>
          <RHFOTPInput
            name="twoFactorCode"
            onOTPComplete={(value) => {
              console.log('OTP completed:', value);
            }}
          />
        </RHFInputGroup>

        <Button type="submit">Verify Code</Button>
      </form>
    </FormProvider>
  );
}

Inline Rules Validation

For simple validation, use the rules prop:

<RHFOTPInput
  name="otp"
  rules={{
    required: 'OTP is required',
    minLength: { value: 6, message: 'OTP must be 6 digits' }
  }}
/>

For complex forms, we recommend using a schema resolver (Zod, Yup) instead.

Length Configuration

Configure the number of OTP input fields using the length prop:

<RHFOTPInput name="pin" length={4} />
<RHFOTPInput name="code" length={6} />
<RHFOTPInput name="activationKey" length={8} />

By default, length is set to 6 digits.

Input Mode

Control whether users can enter only numbers or alphanumeric characters:

<RHFOTPInput name="numericCode" numberOnly={true} />
<RHFOTPInput name="alphanumericCode" numberOnly={false} />

By default, numberOnly is true, restricting input to numeric characters only.

OTP Completion Callback

Use onOTPComplete to perform actions when all OTP fields are filled:

<RHFOTPInput
  name="otp"
  onOTPComplete={(value) => {
    console.log('OTP entered:', value);
    verifyCode(value);
  }}
/>

This callback is triggered automatically when the user completes entering all digits.

Error States

RHFOTPInput automatically displays error states with:

  • Red border via GeckoUIRHFOTPInput--error CSS class when fieldState.error exists
  • Individual input fields inherit error styling

Use RHFInputGroup component for label + input + error layout:

<RHFInputGroup label="Verification Code" required>
  <RHFOTPInput name="code" />
</RHFInputGroup>

Or combine with RHFError component manually:

<RHFOTPInput name="code" />
<RHFError name="code" />

Tip: Use mode: 'onBlur' in useForm to validate on blur for better UX:

const methods = useForm({
  resolver: zodResolver(schema),
  mode: 'onBlur'
});

Validation Patterns

Common Zod validation patterns for OTP inputs:

const schema = z.object({
  sixDigitCode: z.string()
    .length(6, 'Code must be exactly 6 digits')
    .regex(/^\d+$/, 'Code must contain only numbers'),

  fourDigitPin: z.string()
    .length(4, 'PIN must be exactly 4 digits')
    .regex(/^\d+$/, 'PIN must contain only numbers'),

  alphanumericCode: z.string()
    .length(8, 'Code must be exactly 8 characters')
    .regex(/^[A-Za-z0-9]+$/, 'Code must be alphanumeric')
});

Accessibility

  • Automatic focus management between input fields
  • Keyboard navigation support (arrow keys, backspace, delete)
  • Proper ARIA attributes for error states
  • Tab navigation support with smart focus handling
  • Input mode hints for mobile keyboards (numeric vs text)
  • Screen reader compatible

Keyboard Navigation

  • Numbers/Letters: Enter value and auto-advance to next field
  • Backspace/Delete: Remove current value and move to previous field
  • Tab: Navigate to next unfilled field
  • Enter: Submit form when last field is filled
  • Paste: Automatically distribute pasted content across fields