Jack UI logoJackUI
ComponentsOTP Input

Classic OTP Input

A crisp, modern bordered verification input featuring spring-animated character drops, dynamic active slot glow tracking, masking toggle, and built-in error shake physics.

Open in

Security Verification

Enter your code or test with 123456

Resend code in 59s

Installation

bunx shadcn@latest add https://jackui.com/r/classic-otp-input.json
npx shadcn@latest add https://jackui.com/r/classic-otp-input.json
pnpm dlx shadcn@latest add https://jackui.com/r/classic-otp-input.json

The CLI will install motion and lucide-react dependencies and add the component to your project at:

components/jackui/otp-input/classic-otp-input.tsx

Usage

"use client";

import React, { useState } from "react";
import { ClassicOtpInput } from "@/components/jackui/otp-input/classic-otp-input";

export default function ClassicOtpVerification() {
  const [otp, setOtp] = useState("");
  const [isError, setIsError] = useState(false);
  const [isSuccess, setIsSuccess] = useState(false);

  const handleComplete = (code: string) => {
    // Validate the OTP code
    if (code === "123456") {
      setIsSuccess(true);
      setIsError(false);
    } else {
      setIsError(true);
      setIsSuccess(false);
      setTimeout(() => setIsError(false), 2000);
    }
  };

  return (
    <div className="flex flex-col items-center justify-center p-8">
      <ClassicOtpInput
        length={6}
        value={otp}
        onChange={(val) => {
          setOtp(val);
          if (isError) setIsError(false);
        }}
        onComplete={handleComplete}
        isError={isError}
        isSuccess={isSuccess}
        autoFocus
      />
    </div>
  );
}

Props

PropTypeDescription
lengthnumberOptional. Number of OTP digit slots (typically 4, 6, or 8). Default: 6
valuestringOptional. Controlled input value string.
onChange(value: string) => voidOptional. Callback fired whenever the entered value changes.
onComplete(value: string) => voidOptional. Callback fired when all digit slots are filled.
type"number" | "text" | "password"Optional. Input character filter type. Default: "number"
maskbooleanOptional. Whether to mask entered characters with bullet dots. Default: false
disabledbooleanOptional. Disables input interaction and dims the slots. Default: false
autoFocusbooleanOptional. Automatically focuses the first slot on mount. Default: false
isErrorbooleanOptional. Displays error visual state and triggers spring shake animation. Default: false
isSuccessbooleanOptional. Displays celebratory emerald glow on completion. Default: false
size"sm" | "md" | "lg"Optional. Size scale of the input slots (sm, md, lg). Default: "md"
separatorReact.ReactNodeOptional. Custom separator element rendered between digit groups (e.g. dash -).
placeholderstringOptional. Placeholder character displayed in empty slots. Default: ""
classNamestringOptional. Additional container CSS class names.
slotClassNamestringOptional. Additional individual slot CSS class names.
refReact.Ref<ClassicOtpInputRef>Optional. Imperative ref handle exposing focus(), blur(), and clear() methods.