Input OTP

Segmented one-time password and PIN input.

Enter one-time passwords and PINs in segmented fields with paste support. Built on input-otp.

tsx
import {  InputOTP,  InputOTPGroup,  InputOTPSeparator,  InputOTPSlot,} from "@/components/ui/input-otp"export function Example() {  return (    <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>  )}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add input-otp

Usage

import {  InputOTP,  InputOTPGroup,  InputOTPSeparator,  InputOTPSlot,} from "@/components/ui/input-otp"
<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>

Composition

InputOTP
├── InputOTPGroup
│   ├── InputOTPSlot
│   ├── InputOTPSlot
│   └── InputOTPSlot
├── InputOTPSeparator
└── InputOTPGroup
    ├── InputOTPSlot
    ├── InputOTPSlot
    └── InputOTPSlot

Pattern

Use pattern with a helper from input-otp, such as REGEXP_ONLY_DIGITS.

tsx
import { REGEXP_ONLY_DIGITS } from "input-otp"import {  InputOTP,  InputOTPGroup,  InputOTPSlot,} from "@/components/ui/input-otp"export function Example() {  return (    <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS}>      <InputOTPGroup>        <InputOTPSlot index={0} />        <InputOTPSlot index={1} />        <InputOTPSlot index={2} />        <InputOTPSlot index={3} />        <InputOTPSlot index={4} />        <InputOTPSlot index={5} />      </InputOTPGroup>    </InputOTP>  )}

Separator

Place InputOTPSeparator between slot groups.

tsx
import {  InputOTP,  InputOTPGroup,  InputOTPSeparator,  InputOTPSlot,} from "@/components/ui/input-otp"export function Example() {  return (    <InputOTP maxLength={6}>      <InputOTPGroup>        <InputOTPSlot index={0} />        <InputOTPSlot index={1} />      </InputOTPGroup>      <InputOTPSeparator />      <InputOTPGroup>        <InputOTPSlot index={2} />        <InputOTPSlot index={3} />      </InputOTPGroup>      <InputOTPSeparator />      <InputOTPGroup>        <InputOTPSlot index={4} />        <InputOTPSlot index={5} />      </InputOTPGroup>    </InputOTP>  )}

Disabled

Set disabled to prevent input.

tsx
import { Label } from "@/components/ui/label"import {  InputOTP,  InputOTPGroup,  InputOTPSeparator,  InputOTPSlot,} from "@/components/ui/input-otp"export function Example() {  return (    <div className="flex flex-col gap-2">      <Label htmlFor="otp-disabled">Verification code</Label>      <InputOTP id="otp-disabled" maxLength={6} disabled>        <InputOTPGroup>          <InputOTPSlot index={0} />          <InputOTPSlot index={1} />          <InputOTPSlot index={2} />        </InputOTPGroup>        <InputOTPSeparator />        <InputOTPGroup>          <InputOTPSlot index={3} />          <InputOTPSlot index={4} />          <InputOTPSlot index={5} />        </InputOTPGroup>      </InputOTP>    </div>  )}

Controlled

Control the value with value and onChange.

Enter your one-time password.
tsx
"use client"import * as React from "react"import { Label } from "@/components/ui/label"import {  InputOTP,  InputOTPGroup,  InputOTPSlot,} from "@/components/ui/input-otp"export function Example() {  const [value, setValue] = React.useState("")  return (    <div className="flex flex-col items-center gap-2">      <Label htmlFor="otp-controlled">Enter your one-time password.</Label>      <InputOTP        id="otp-controlled"        maxLength={6}        value={value}        onChange={setValue}      >        <InputOTPGroup>          <InputOTPSlot index={0} />          <InputOTPSlot index={1} />          <InputOTPSlot index={2} />          <InputOTPSlot index={3} />          <InputOTPSlot index={4} />          <InputOTPSlot index={5} />        </InputOTPGroup>      </InputOTP>      <div className="text-sm tracking-wide text-muted-foreground">        {value === "" ? "Enter your one-time password." : `You entered: ${value}`}      </div>    </div>  )}

Invalid

Set aria-invalid on slots for an error state.

0
0
0
0
0
0
tsx
"use client"import * as React from "react"import {  InputOTP,  InputOTPGroup,  InputOTPSlot,} from "@/components/ui/input-otp"export function Example() {  const [value, setValue] = React.useState("000000")  return (    <InputOTP maxLength={6} value={value} onChange={setValue}>      <InputOTPGroup>        <InputOTPSlot index={0} aria-invalid />        <InputOTPSlot index={1} aria-invalid />        <InputOTPSlot index={2} aria-invalid />        <InputOTPSlot index={3} aria-invalid />        <InputOTPSlot index={4} aria-invalid />        <InputOTPSlot index={5} aria-invalid />      </InputOTPGroup>    </InputOTP>  )}

Four Digits

Create a four-digit PIN field with maxLength={4} and REGEXP_ONLY_DIGITS.

tsx
import { REGEXP_ONLY_DIGITS } from "input-otp"import {  InputOTP,  InputOTPGroup,  InputOTPSlot,} from "@/components/ui/input-otp"export function Example() {  return (    <InputOTP maxLength={4} pattern={REGEXP_ONLY_DIGITS}>      <InputOTPGroup>        <InputOTPSlot index={0} />        <InputOTPSlot index={1} />        <InputOTPSlot index={2} />        <InputOTPSlot index={3} />      </InputOTPGroup>    </InputOTP>  )}

Alphanumeric

REGEXP_ONLY_DIGITS_AND_CHARS accepts letters and numbers. Set inputMode="text" to show the full keyboard on mobile; the library defaults to numeric.

tsx
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"import {  InputOTP,  InputOTPGroup,  InputOTPSlot,} from "@/components/ui/input-otp"export function Example() {  return (    <InputOTP      maxLength={6}      pattern={REGEXP_ONLY_DIGITS_AND_CHARS}      inputMode="text"    >      <InputOTPGroup>        <InputOTPSlot index={0} />        <InputOTPSlot index={1} />        <InputOTPSlot index={2} />        <InputOTPSlot index={3} />        <InputOTPSlot index={4} />        <InputOTPSlot index={5} />      </InputOTPGroup>    </InputOTP>  )}

API Reference

See the input-otp documentation for the full set of root props, including maxLength, pattern, value, onChange, and onComplete.

InputOTP

PropTypeDefault
maxLengthnumber
patternstring
valuestring
onChange(value: string) => void
disabledboolean
containerClassNamestring
classNamestring
<InputOTP maxLength={6}>  <InputOTPGroup>    <InputOTPSlot index={0} />    <InputOTPSlot index={1} />    <InputOTPSlot index={2} />  </InputOTPGroup></InputOTP>

InputOTPSlot

PropTypeDefault
indexnumber
classNamestring

Native div attributes, including aria-invalid, are supported.

<InputOTPSlot index={0} />

InputOTPGroup

<InputOTPGroup>  <InputOTPSlot index={0} /></InputOTPGroup>

InputOTPSeparator

<InputOTPSeparator />