Skip to main content

input otp

Accessible one-time password component with paste-to-fill support.

Philosophy

OTP inputs are deceptively complex: they need to handle paste, auto-fill, focus management across segments, and mobile keyboard optimization. We isolate this complexity in a dedicated component rather than stretching Input to cover it. Each slot is its own element, giving you full control over styling and animation per digit.

How It's Built

Loading diagram...

Installation


npx @gentleduck/cli add input-otp

npx @gentleduck/cli add input-otp

Usage

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
<InputOTP maxLength={6}>
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
</InputOTP>
<InputOTP maxLength={6}>
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
</InputOTP>

Paste

  • Pasting fills slots starting at the focused input.
  • Characters are filtered by the pattern prop.
  • onValueChange fires after paste and per-key entry.

Examples

Pattern

Use the pattern prop to define a custom pattern for the OTP input.

 
...
 
<InputOTP maxLength={6}>
  <InputOTPGroup>
    <InputOTPSlot />
    {/* ... */}
  </InputOTPGroup>
</InputOTP>
 
...
 
<InputOTP maxLength={6}>
  <InputOTPGroup>
    <InputOTPSlot />
    {/* ... */}
  </InputOTPGroup>
</InputOTP>

Separator

You can use the <InputOTPSeparator /> component to add a separator between the input groups.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
 
...
 
<InputOTP maxLength={4}>
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
</InputOTP>
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
 
...
 
<InputOTP maxLength={4}>
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
</InputOTP>

Controlled

You can use the value and onValueChange props to control the input value.

Custom separator

Form

API Reference

InputOTP

The root component that provides OTP context and manages slot focus, paste, and value state.

PropTypeDefaultDescription
classNamestring--Additional class names for the wrapper div
childrenReact.ReactNode--OTP groups, slots, and separators
valuestring--Controlled value of the OTP input
onValueChange(value: string) => void--Callback fired when the combined slot value changes
patternRegExp/^.$/Regex used to validate each entered character
maxLengthnumber--Optional cap for active slots used by keyboard and paste behavior
namestring--Optional field name (useful with form libraries)
dir'ltr' | 'rtl'inheritedLocal direction override
refReact.Ref<HTMLDivElement>--Ref forwarded to the wrapper div
aria-labelstring"otp-one-time-password"Accessible label for the OTP region
...propsOmit<React.HTMLProps<HTMLDivElement>, 'pattern'>--Additional props to spread to the content div

InputOTPGroup

Groups related OTP slots together with flex layout.

PropTypeDefaultDescription
classNamestring--Additional class names for the group div
childrenReact.ReactNode--OTP slots to group together
refReact.Ref<HTMLDivElement>--Ref forwarded to the group div
...propsReact.HTMLProps<HTMLDivElement>--Additional props to spread to the content div

InputOTPSlot

An individual input element representing a single OTP digit.

PropTypeDefaultDescription
classNamestring--Additional class names for the input element
refReact.Ref<HTMLInputElement>--Ref forwarded to the input element
...propsReact.HTMLProps<HTMLInputElement>--Additional props to spread to the input element

InputOTPSeparator

A visual separator rendered between OTP groups.

PropTypeDefaultDescription
classNamestring--Additional class names for the separator div
customIndicatorReact.ReactNode<Dot />Custom element to render instead of the default dot icon
refReact.Ref<HTMLDivElement>--Ref forwarded to the separator div
...propsReact.HTMLProps<HTMLDivElement>--Additional props to spread to the content div

RTL Support

Direction is resolved through the shared primitives direction module. Use a local dir="rtl" override when the component exposes it, or set DirectionProvider at app/root level for global RTL/LTR behavior.