Ceasefire now! 🕊️🇵🇸
Brokarim's UI creation notes!

input otp

Demo

The OTPInput component from the OTP input library is a customizable One-time password input component for React. It allows you to:

  • Set the maximum length of the OTP input
  • Customize the container class name, including the ability to apply styles when the input is disabled
  • Render the OTP input slots in a flexible way, such as splitting them into two rows with a fake dash in between

With this component, you can build a feature-rich and visually appealing OTP input interface for your React applications, with the ability to tailor the appearance and behavior to match your specific design and user experience requirements.

Installation

npm install react-tweet

Update tailwind.config.js

const config = {
  theme: {
    extend: {
      keyframes: {
        'caret-blink': {
          '0%,70%,100%': { opacity: '1' },
          '20%,50%': { opacity: '0' },
        },
      },
      animation: {
        'caret-blink': 'caret-blink 1.2s ease-out infinite',
      },
    },
  },
}

Copy paste this utils.ts to merge class names.

import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
 
import type { ClassValue } from "clsx";
 
export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Usage

import { OTPInput, SlotProps } from "input-otp";
import { cn } from "@/lib/utils";
 
export default function OtpInput() {
  return (
    <>
      <OTPInput
        maxLength={6}
        containerClassName="group flex items-center has-[:disabled]:opacity-30"
        render={({ slots }) => (
          <>
            <div className="flex">
              {slots.slice(0, 3).map((slot, idx) => (
                <Slot key={idx} {...slot} />
              ))}
            </div>
 
            <FakeDash />
 
            <div className="flex">
              {slots.slice(3).map((slot, idx) => (
                <Slot key={idx} {...slot} />
              ))}
            </div>
          </>
        )}
      />
    </>
  );
}
 
function Slot(props: SlotProps) {
  return (
    <div
      className={cn(
        "relative w-10 h-14 text-[2rem]",
        "flex items-center justify-center",
        "transition-all duration-300",
        "border-border border-y border-r first:border-l first:rounded-l-md last:rounded-r-md",
        "group-hover:border-accent-foreground/20 group-focus-within:border-accent-foreground/20",
        "outline outline-0 outline-accent-foreground/20",
        { "outline-4 outline-accent-foreground": props.isActive }
      )}
    >
      {props.char !== null && <div>{props.char}</div>}
      {props.hasFakeCaret && <FakeCaret />}
    </div>
  );
}
 
// You can emulate a fake textbox caret!
function FakeCaret() {
  return (
    <div className="absolute pointer-events-none inset-0 flex items-center justify-center animate-caret-blink">
      <div className="w-px h-8 bg-white" />
    </div>
  );
}
 
// Inspired by Stripe's MFA input.
function FakeDash() {
  return (
    <div className="flex w-10 justify-center items-center">
      <div className="w-3 h-1 rounded-full bg-border" />
    </div>
  );
}
Updated:
Author:

On this page