Docs
Input OTP

Input OTP

A specialized input component designed for entering one-time passwords (OTPs), featuring accessibility enhancements, automatic focus shifting between inputs, and seamless copy-paste support.

Loading...

Component Foundation: input-otp

This Input OTP component is built upon the dedicated input-otp library, developed by Guilherme Rodz. This underlying library provides the core logic for handling OTP input behavior, including focus management, pasting, and accessibility.

Installation Procedure

Run the following command:

npx shadcn@latest add input-otp

Update tailwind.config.js

Add the following animations to your tailwind.config.js file:

/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    extend: {
      keyframes: {
        "caret-blink": {
          "0%,70%,100%": { opacity: "1" },
          "20%,50%": { opacity: "0" },
        },
      },
      animation: {
        "caret-blink": "caret-blink 1.25s ease-out infinite",
      },
    },
  },
};

Implementing the Input OTP Component

  InputOTP, // The main container component managing the OTP input state and logic.
  InputOTPGroup, // A container used to group multiple InputOTPSlot components.
  InputOTPSeparator, // Optional component to render a visual separator between groups.
  InputOTPSlot, // Represents a single character input slot within the OTP field.
} from "@/components/ui/input-otp"; // Adjust import path as necessary.
// Basic structure for a 6-digit OTP input with a separator
<InputOTP maxLength={6}>
  {/* First group of input slots */}
  <InputOTPGroup>
    <InputOTPSlot index={0} /> {/* Slot for the first digit */}
    <InputOTPSlot index={1} /> {/* Slot for the second digit */}
    <InputOTPSlot index={2} /> {/* Slot for the third digit */}
  </InputOTPGroup>
  {/* Optional separator */}
  <InputOTPSeparator />
  {/* Second group of input slots */}
  <InputOTPGroup>
    <InputOTPSlot index={3} /> {/* Slot for the fourth digit */}
    <InputOTPSlot index={4} /> {/* Slot for the fifth digit */}
    <InputOTPSlot index={5} /> {/* Slot for the sixth digit */}
  </InputOTPGroup>
</InputOTP>

The component uses a compositional approach. The main <InputOTP> component defines the maximum length. Inside, <InputOTPGroup> wraps sets of <InputOTPSlot> components. Each <InputOTPSlot> requires a unique index prop corresponding to its position in the sequence (0-based). An optional <InputOTPSeparator> can be placed between groups for visual distinction.

Input OTP Variations and Examples

Custom Input Pattern (Regex)

Control the allowed characters in the OTP input by providing a regular expression to the pattern prop on the <InputOTP> component. The input-otp library exports common patterns like REGEXP_ONLY_DIGITS_AND_CHARS.

Loading...
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"

...

<InputOTP
  maxLength={6}
  pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    {/* ... */}
  </InputOTPGroup>
</InputOTP>

Using a Visual Separator

Enhance readability for longer OTPs by inserting the <InputOTPSeparator /> component between <InputOTPGroup> elements. This renders a visual separator (often a dash or space, depending on styling) without being an actual input slot.

Loading...
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

...

<InputOTP maxLength={4}>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot index={2} />
    <InputOTPSlot index={3} />
  </InputOTPGroup>
</InputOTP>

Controlled Input Component

Manage the OTP input's value externally using React state by providing the value and onChange props to the <InputOTP> component, similar to standard controlled inputs in React.

Loading...

Integration within Forms

Integrate the <InputOTP> component into forms built with libraries like React Hook Form. Use the <FormField> component to connect the OTP input to the form state and handle validation.

Loading...

Component Changelog and Updates

2024-03-19 Composition

We've made some updates and replaced the render props pattern with composition. Here's how to update your code if you prefer the composition pattern.

Update to the latest version of input-otp.

npm install input-otp@latest

Update input-otp.tsx

- import { OTPInput, SlotProps } from "input-otp"
+ import { OTPInput, OTPInputContext } from "input-otp"

 const InputOTPSlot = React.forwardRef<
   React.ElementRef<"div">,
-   SlotProps & React.ComponentPropsWithoutRef<"div">
-  >(({ char, hasFakeCaret, isActive, className, ...props }, ref) => {
+   React.ComponentPropsWithoutRef<"div"> & { index: number }
+  >(({ index, className, ...props }, ref) => {
+   const inputOTPContext = React.useContext(OTPInputContext)
+   const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]

Then replace the render prop in your code.

<InputOTP maxLength={6}>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
    <InputOTPSlot index={2} />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot index={3} />
    <InputOTPSlot index={4} />
    <InputOTPSlot index={5} />
  </InputOTPGroup>
</InputOTP>

2024-03-19 Disabled

To add a disabled state to the input, update <InputOTP /> as follows:

const InputOTP = React.forwardRef<
  React.ElementRef<typeof OTPInput>,
  React.ComponentPropsWithoutRef<typeof OTPInput>
>(({ className, containerClassName, ...props }, ref) => (
  <OTPInput
    ref={ref}
    containerClassName={cn(
      "flex items-center gap-2 has-[:disabled]:opacity-50",
      containerClassName,
    )}
    className={cn("disabled:cursor-not-allowed", className)}
    {...props}
  />
));
InputOTP.displayName = "InputOTP";
    Input OTP