Components
- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Avatar
- Badge
- Breadcrumb
- Button
- Calendar
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- Command
- Date Picker
- Dialog
- DrawerNew
- Empty
- Field
- Fieldset
- Form
- Frame
- Group
- Input
- Input Group
- Input OTPNew
- Kbd
- Label
- Menu
- Meter
- Number Field
- Pagination
- Popover
- Preview Card
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Toolbar
- Tooltip
Resources
Input OTP
A segmented input for one-time passwords and verification codes.
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
export default function Particle() {
return (
<InputOTP aria-label="One-time password" maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
);
}
Installation
pnpm dlx shadcn@latest add @coss/input-otp
Usage
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"<InputOTP aria-label="Verification code" 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>API Reference
This component is built on top of input-otp.
InputOTP
Root component for the OTP input. Accepts all props from OTPInput plus the following:
| Prop | Type | Default | Description |
|---|---|---|---|
containerClassName | string | - | Class name applied to the outer OTP container |
For the full list of props, see the input-otp documentation.
InputOTPGroup
Container for one or more OTP slots.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "default" | "lg" | "default" | Size applied to the group's slots |
InputOTPSlot
Renders a single OTP slot for the provided index.
| Prop | Type | Default | Description |
|---|---|---|---|
index | number | - | Zero-based slot index within the parent InputOTP |
InputOTPSeparator
Visual separator between slot groups.
Examples
Large
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
export default function Particle() {
return (
<InputOTP aria-label="One-time password" maxLength={4}>
<InputOTPGroup size="lg">
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
</InputOTPGroup>
</InputOTP>
);
}
With Separator
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp";
export default function Particle() {
return (
<InputOTP aria-label="Verification code" 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>
);
}
With Label
Enter the 4-digit code sent to your email.
import { useId } from "react";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { Label } from "@/components/ui/label";
export default function Particle() {
const id = useId();
return (
<div className="flex flex-col items-center gap-2">
<Label htmlFor={id}>Verification code</Label>
<InputOTP aria-label="Verification code" id={id} maxLength={4}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
</InputOTPGroup>
</InputOTP>
<p className="text-muted-foreground text-xs">
Enter the 4-digit code sent to your email.
</p>
</div>
);
}
Digits Only
"use client";
import { REGEXP_ONLY_DIGITS } from "input-otp";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
export default function Particle() {
return (
<InputOTP
aria-label="Verification code"
inputMode="numeric"
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>
);
}
Invalid
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
export default function Particle() {
return (
<div className="flex flex-col items-center gap-2">
<InputOTP aria-invalid aria-label="Verification code" maxLength={6}>
<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>
</div>
);
}
Auto Validation
Enter `123456` to pass validation.
"use client";
import { useId, useState } from "react";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { Label } from "@/components/ui/label";
export default function Particle() {
const id = useId();
const [value, setValue] = useState("");
const [invalid, setInvalid] = useState(false);
const valid = value.length === 6 && value === "123456";
return (
<div className="flex flex-col items-center gap-2">
<Label htmlFor={id}>Verification code</Label>
<InputOTP
aria-label="Verification code"
id={id}
maxLength={6}
onChange={(nextValue) => {
setValue(nextValue);
setInvalid(nextValue.length === 6 ? nextValue !== "123456" : false);
}}
value={value}
>
<InputOTPGroup>
<InputOTPSlot aria-invalid={invalid || undefined} index={0} />
<InputOTPSlot aria-invalid={invalid || undefined} index={1} />
<InputOTPSlot aria-invalid={invalid || undefined} index={2} />
<InputOTPSlot aria-invalid={invalid || undefined} index={3} />
<InputOTPSlot aria-invalid={invalid || undefined} index={4} />
<InputOTPSlot aria-invalid={invalid || undefined} index={5} />
</InputOTPGroup>
</InputOTP>
{!valid && !invalid && (
<p className="text-muted-foreground text-xs">
Enter `123456` to pass validation.
</p>
)}
{invalid && (
<p className="text-destructive-foreground text-xs">
Code must be 123456.
</p>
)}
{valid && (
<p className="text-success-foreground text-xs">Code verified.</p>
)}
</div>
);
}