OTP

The Otp component allows you to implement a One-Time Password (OTP) verification UI. With the customUIRender prop, you can fully customize the look and behavior of the OTP interface to match your project's needs.

Installation Method

You can use the following command to install the extension with the latest plugins:

npx @akinon/projectzero@latest --plugins

Props

Prop
Type
Required
Description

customUIRender

React.ReactNode

Optional

A render function to override the default OTP interface. Provides access to UI and control handlers

submitAction

SubmitHandler<{ [key: string]: any }>

Yes

A submit handler function triggered when OTP form is submitted.

data

{ [key: string]: any }

Yes

Data object containing required fields like phone number. OTP code will be appended here before submission

CustomUIRender Parameters

Parameter
Type
Description

closeHandler

() => void

Closes the popup manually.

resendHandler

() => void

Triggers OTP resend functionality.

onSubmit

(event: FormEvent<HTMLFormElement>) => Promise<void>

Form submit function that wraps validation and calls submitAction.

otp

string

Current OTP value in the input field.

setOtp

(otp: string) => void

Updates the OTP input value.

hasError

boolean

Whether the current OTP input has a validation error.

error

string

Error message for invalid OTP attempts.

canResend

boolean

Indicates whether the resend option is enabled.

time

number

Countdown in seconds for the resend option.

codeLength

number

Expected number of digits in the OTP input.

setHasError

(hasError: boolean) => void

Allows toggling of error state manually.

Usage Example

Default Usage

import PluginModule, { Component } from '@akinon/next/components/plugin-module';

<PluginModule
   component={Component.Otp}
   props={{
     data: getValues(),
     submitAction: registerHandler
   }}

 />

Customizing OTP

import PluginModule, { Component } from '@akinon/next/components/plugin-module';

<PluginModule
  component={Component.Otp}
  props={{
    data: getValues(),
    submitAction: registerHandler,
    customUIRender: ({
      closeHandler,
      resendHandler,
      onSubmit,
      otp,
      setOtp,
      hasError,
      error,
      canResend,
      time,
      codeLength,
      setHasError
    }) => {
      return (
        <div className="fixed left-0 top-0 z-50 flex h-screen w-screen items-end md:items-center md:justify-center md:bg-black/10">
          <div className="h-[calc(100vh-48px)] w-screen flex md:h-auto md:max-w-lg flex-col items-center rounded-sm bg-white p-8 shadow-xl">
            <div className="w-full flex items-center justify-end">
              <div className="cursor-pointer" onClick={closeHandler}>
                <svg
                  width="14"
                  height="14"
                  viewBox="0 0 14 14"
                  xmlns="http://www.w3.org/2000/svg"
                >
                  <g fill="#000" fillRule="nonzero">
                    <path d="M.684 14A.684.684 0 0 1 .2 12.833L12.833.2a.684.684 0 1 1 .967.967L1.167 13.8a.682.682 0 0 1-.483.2z" />
                    <path d="M13.316 14a.682.682 0 0 1-.483-.2L.2 1.167A.684.684 0 0 1 1.167.2L13.8 12.833A.684.684 0 0 1 13.316 14z" />
                  </g>
                </svg>
              </div>
            </div>
            <div className="flex flex-col items-center px-14 mt-5">
              <div className="text-2xl font-medium">OTP Verification</div>
              <div className="mt-2.5 text-center text-gray-700">
                {`Please enter the ${codeLength}-digit sms code sent to your registered number and email address`}
              </div>
            </div>
            <form
              onSubmit={onSubmit}
              className="flex flex-col items-center w-full"
            >
              <OtpInput
                value={otp}
                onChange={(otp) => {
                  setOtp(otp);
                  setHasError(false);
                }}
                numInputs={codeLength}
                containerStyle="mt-12 gap-2 flex-wrap"
                inputStyle={`h-12 w-8 md:h-16 md:w-12 rounded-md border border-gray-600 text-center text-lg ${hasError ? 'border-error' : ''}`}
                renderInput={(props) => <input {...props} />}
                skipDefaultStyles={true}
              />
              {error && <p className="text-xs text-error mt-2">{error}</p>}
              <Button
                type="submit"
                className="mt-5 h-auto w-full py-4 text-lg font-medium uppercase"
              >
                Verify
              </Button>
            </form>
            <div className="mt-6 flex flex-col items-center">
              <span className="text-gray-700">I didn’t receive a code</span>
              <div
                className={`font-medium underline cursor-pointer ${!canResend ? 'cursor-not-allowed text-gray-700' : ''}`}
                onClick={resendHandler}
              >
                RESEND
              </div>
            </div>
          </div>
        </div>
      );
    }
  }}
/>;

Last updated

Was this helpful?