MC Input OTP

A secure one-time password input component.

Overview

The mc-input-otp is a secure input component for entering one-time passwords (OTP). Built on the input-otp library, it provides a smooth typing experience with support for grouping slots and visual separators.

As a part of the MicroClub library, this component is designed for instant deployment into your codebase, giving you full ownership of its look and behavior.

Preview

Loading...
import {  McInputOtp,  McInputOtpGroup,  McInputOtpSeparator,  McInputOtpSlot,} from '../ui/mc-input-otp';export default function McInputOtpDemo() {  return (    <McInputOtp maxLength={6}>      <McInputOtpGroup>        <McInputOtpSlot index={0} />        <McInputOtpSlot index={1} />        <McInputOtpSlot index={2} />      </McInputOtpGroup>      <McInputOtpSeparator />      <McInputOtpGroup>        <McInputOtpSlot index={3} />        <McInputOtpSlot index={4} />        <McInputOtpSlot index={5} />      </McInputOtpGroup>    </McInputOtp>  );}

Add to Your Project

Deploy the mc-input-otp directly to your components/ui directory:

npx mcoli-ui@latest add mc-input-otp
pnpm dlx mcoli-ui@latest add mc-input-otp
yarn dlx mcoli-ui@latest add mc-input-otp
bunx mcoli-ui@latest add mc-input-otp

Manual Setup

If you prefer manual control:

Install the core dependencies
npm install clsx tailwind-merge input-otp lucide-react
pnpm add clsx tailwind-merge input-otp lucide-react
yarn add clsx tailwind-merge input-otp lucide-react
bun add clsx tailwind-merge input-otp lucide-react
Copy the source code
'use client';import * as React from 'react';import { OTPInput, OTPInputContext } from 'input-otp';import { cn } from '@/lib/utils';import { MinusIcon } from 'lucide-react';function McInputOtp({  className,  containerClassName,  ...props}: React.ComponentProps<typeof OTPInput> & {  containerClassName?: string;}) {  return (    <OTPInput      data-slot="input-otp"      containerClassName={cn('flex items-center has-disabled:opacity-50', containerClassName)}      spellCheck={false}      className={cn('disabled:cursor-not-allowed', className)}      {...props}    />  );}function McInputOtpGroup({ className, ...props }: React.ComponentProps<'div'>) {  return (    <div data-slot="input-otp-group" className={cn('flex items-center', className)} {...props} />  );}function McInputOtpSlot({  index,  className,  ...props}: React.ComponentProps<'div'> & {  index: number;}) {  const inputOTPContext = React.useContext(OTPInputContext);  const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};  return (    <div      data-slot="input-otp-slot"      data-active={isActive}      className={cn(        'relative flex size-10 items-center justify-center border border-border bg-input text-sm transition-all outline-none first:rounded-l-md last:rounded-r-md last:mr-0 not-last:-mr-px data-[active=true]:z-10 data-[active=true]:border-transparent data-[active=true]:ring-2 data-[active=true]:ring-border',        className      )}      {...props}    >      {char}      {hasFakeCaret && (        <div className="pointer-events-none absolute inset-0 flex items-center justify-center">          <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />        </div>      )}    </div>  );}function McInputOtpSeparator({ ...props }: React.ComponentProps<'div'>) {  return (    <div      data-slot="input-otp-separator"      className="flex items-center mx-2"      role="separator"      {...props}    >      <MinusIcon size={24} strokeWidth={1.25} className="text-muted-foreground" />    </div>  );}export { McInputOtp, McInputOtpGroup, McInputOtpSlot, McInputOtpSeparator };
Adjust import paths

Ensure your @/lib/utils or equivalent classname helper is correctly imported.

Usage

import {
  McInputOtp,
  McInputOtpGroup,
  McInputOtpSeparator,
  McInputOtpSlot,
} from '@/components/ui/mc-input-otp';

export default function Example() {
  return (
    <McInputOtp maxLength={6}>
      <McInputOtpGroup>
        <McInputOtpSlot index={0} />
        <McInputOtpSlot index={1} />
        <McInputOtpSlot index={2} />
      </McInputOtpGroup>
      <McInputOtpSeparator />
      <McInputOtpGroup>
        <McInputOtpSlot index={3} />
        <McInputOtpSlot index={4} />
        <McInputOtpSlot index={5} />
      </McInputOtpGroup>
    </McInputOtp>
  );
}

Examples

With Separator

Group OTP slots with a visual separator between groups.

<McInputOtp maxLength={6}>
  <McInputOtpGroup>
    <McInputOtpSlot index={0} />
    <McInputOtpSlot index={1} />
    <McInputOtpSlot index={2} />
  </McInputOtpGroup>
  <McInputOtpSeparator />
  <McInputOtpGroup>
    <McInputOtpSlot index={3} />
    <McInputOtpSlot index={4} />
    <McInputOtpSlot index={5} />
  </McInputOtpGroup>
</McInputOtp>

Without Separator

Use a single group for continuous OTP input.

<McInputOtp maxLength={6}>
  <McInputOtpGroup>
    <McInputOtpSlot index={0} />
    <McInputOtpSlot index={1} />
    <McInputOtpSlot index={2} />
    <McInputOtpSlot index={3} />
    <McInputOtpSlot index={4} />
    <McInputOtpSlot index={5} />
  </McInputOtpGroup>
</McInputOtp>

Multiple Separators

Create custom groupings with multiple separators.

<McInputOtp maxLength={6}>
  <McInputOtpGroup>
    <McInputOtpSlot index={0} />
    <McInputOtpSlot index={1} />
  </McInputOtpGroup>
  <McInputOtpSeparator />
  <McInputOtpGroup>
    <McInputOtpSlot index={2} />
    <McInputOtpSlot index={3} />
  </McInputOtpGroup>
  <McInputOtpSeparator />
  <McInputOtpGroup>
    <McInputOtpSlot index={4} />
    <McInputOtpSlot index={5} />
  </McInputOtpGroup>
</McInputOtp>

API Reference

Props

PropTypeDefaultDescription
maxLengthnumberundefinedThe maximum number of OTP characters
containerClassNamestringundefinedAdditional CSS classes for the container
classNamestringundefinedAdditional CSS classes

All other props from input-otp are supported.

Components

ComponentDescription
McInputOtpThe root OTP input container
McInputOtpGroupA group of OTP slots
McInputOtpSlotIndividual OTP input slot
McInputOtpSeparatorVisual separator between groups

McInputOtpSlot Props

PropTypeDefaultDescription
indexnumberrequiredThe index of the slot (0-based)
classNamestringundefinedAdditional CSS classes

On this page