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
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:
Manual Setup
If you prefer manual control:
'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 };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
| Prop | Type | Default | Description |
|---|---|---|---|
maxLength | number | undefined | The maximum number of OTP characters |
containerClassName | string | undefined | Additional CSS classes for the container |
className | string | undefined | Additional CSS classes |
All other props from input-otp are supported.
Components
| Component | Description |
|---|---|
McInputOtp | The root OTP input container |
McInputOtpGroup | A group of OTP slots |
McInputOtpSlot | Individual OTP input slot |
McInputOtpSeparator | Visual separator between groups |
McInputOtpSlot Props
| Prop | Type | Default | Description |
|---|---|---|---|
index | number | required | The index of the slot (0-based) |
className | string | undefined | Additional CSS classes |