Jack UI logoJackUI
ComponentsOTP Input

Filled OTP Input

A soft-tinted, elevated tactile tile input designed for modern Fintech banking, transaction approvals, and high-security PIN entry.

Open in

Transaction PIN

Authorize transfer of $250.00

Test PIN: 4321

•
•
•
•
Never share your transaction PIN

Component Overview & Selection Guide

Ideal for financial applications, banking apps, checkout confirmation flows, and crypto wallet approvals where tactile, rounded filled buttons convey security and high fidelity.

Primary Use Cases:

  • Transaction Approvals: Confirm payments, wire transfers, or credit card authorizations.
  • Fintech Security PINs: High-security 4 or 6-digit transaction PIN verification.
  • Lock Screen PIN Pads: Tactile PIN entry on mobile and web applications.

Features

  • Tactile Tile Elevation: Active slot scales up slightly with smooth spring bounce and subtle shadow.
  • High-Contrast Soft Tints: Beautiful neutral backgrounds for both light and dark mode.
  • Fintech Mask Mode: Secure masked bullets default for transaction PIN entry.
  • Vibration & Glow Feedback: Red vibration on incorrect PIN and emerald glow on verified transaction.

Installation

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

The CLI will automatically install the motion and lucide-react dependencies and copy the component into your project at:

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

Usage

"use client";

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

export default function FilledPinVerification() {
  const [pin, setPin] = useState("");
  const [isError, setIsError] = useState(false);
  const [isSuccess, setIsSuccess] = useState(false);

  return (
    <div className="flex flex-col items-center justify-center p-8">
      <FilledOtpInput
        length={4}
        value={pin}
        onChange={setPin}
        onComplete={(val) => {
          if (val === "4321") setIsSuccess(true);
          else setIsError(true);
        }}
        mask={true}
        isError={isError}
        isSuccess={isSuccess}
        autoFocus
      />
    </div>
  );
}

Props

PropTypeDefaultDescription
lengthnumber6Total number of PIN digit slots.
valuestringundefinedControlled value.
onChange(value: string) => voidundefinedCallback fired on value change.
onComplete(value: string) => voidundefinedCallback fired on all slots filled.
type"number" | "text" | "password""number"Character mode.
maskbooleanfalseMask characters as solid dots.
disabledbooleanfalseDisable interactions.
autoFocusbooleanfalseAuto-focus on mount.
isErrorbooleanfalseError vibration & red tint.
isSuccessbooleanfalseSuccess emerald tint.
size"sm" | "md" | "lg""md"Size scale.
placeholderstring"•"Placeholder character in empty slots.
classNamestring""Container CSS classes.
slotClassNamestring""Individual slot CSS classes.