Capture a short, fixed-length one-time passcode (OTP) — for two-factor codes,
email verification codes, magic-link codes, and similar flows. Built on top of
input-otp, it renders a single
hidden input that handles paste, autofill, and IME correctly while displaying
each character in its own styled slot.
1import { OtpInput } from "@ngrok/mantle/otp-input";2 3<OtpInput.Root maxLength={6}>4 <OtpInput.Group>5 <OtpInput.Slot index={0} />6 <OtpInput.Slot index={1} />7 <OtpInput.Slot index={2} />8 </OtpInput.Group>9 <OtpInput.Separator />10 <OtpInput.Group>11 <OtpInput.Slot index={3} />12 <OtpInput.Slot index={4} />13 <OtpInput.Slot index={5} />14 </OtpInput.Group>15</OtpInput.Root>;Render all slots in a single group with no separator.
1<OtpInput.Root maxLength={4}>2 <OtpInput.Group>3 <OtpInput.Slot index={0} />4 <OtpInput.Slot index={1} />5 <OtpInput.Slot index={2} />6 <OtpInput.Slot index={3} />7 </OtpInput.Group>8</OtpInput.Root>Restrict input to numeric characters using the pattern prop and the
REGEXP_ONLY_DIGITS helper.
1import { OtpInput, REGEXP_ONLY_DIGITS } from "@ngrok/mantle/otp-input";2 3<OtpInput.Root maxLength={6} pattern={REGEXP_ONLY_DIGITS}>4 <OtpInput.Group>5 <OtpInput.Slot index={0} />6 <OtpInput.Slot index={1} />7 <OtpInput.Slot index={2} />8 <OtpInput.Slot index={3} />9 <OtpInput.Slot index={4} />10 <OtpInput.Slot index={5} />11 </OtpInput.Group>12</OtpInput.Root>;Pass disabled to the root to disable the entire input.
1<OtpInput.Root maxLength={6} disabled>2 {/* ... */}3</OtpInput.Root>OtpInput.Group and OtpInput.Separator accept an asChild prop. When true,
the part renders its single child instead of its default div, forwarding all
class names, data-* attributes, and the ref onto that child. Use this when you
need a different underlying element — for example, rendering the separator as a
semantic span, or wrapping a group in a styled label.
1<OtpInput.Separator asChild>2 <span aria-hidden>·</span>3</OtpInput.Separator>Compose the parts of an OtpInput together to build your own:
OtpInput.Root├── OtpInput.Group│ └── OtpInput.Slot├── OtpInput.Separator└── OtpInput.Group └── OtpInput.SlotThe root of the OTP input. Wraps the hidden input that captures keystrokes,
paste, and autofill, and provides per-slot state to descendant OtpInput.Slot
parts via context.
All props from the underlying OTPInput component, including standard input props, plus:
Groups one or more OtpInput.Slot parts into a visually-connected segment with shared rounded corners and joined borders.
All props from div, plus:
A single character slot. Must be rendered inside an OtpInput.Root. Reads its character, active state, and fake caret position from the root via context.
All props from div, plus:
A visual separator between two OtpInput.Group segments. Renders a minus icon by default; pass children to override.
All props from div, plus:
Re-exported from input-otp for convenience: