elements
Demos UI Feed Discuss Install
Style System
  • Style
  • Themes
  • Behaviors
Forms
  • Button
  • ButtonGroup
  • Checkbox
  • ColorPicker
  • Combobox
  • Field
  • FileUpload
  • Input
  • InputGroup
  • InputOTP
  • Label
  • NativeSelect
  • NumberInput
  • PasswordInput
  • RadioGroup
  • Select
  • Slider
  • Switch
  • TagsInput
  • Textarea
  • Toggle
  • ToggleGroup
Overlays
  • AlertDialog
  • Command
  • ContextMenu
  • Dialog
  • Drawer
  • DropdownMenu
  • HoverCard
  • Menubar
  • NavigationMenu
  • Popover
  • Sheet
  • Toast
  • Tooltip
Display
  • Accordion
  • Alert
  • AspectRatio
  • Avatar
  • Badge
  • Breadcrumb
  • Card
  • Collapsible
  • Empty
  • Item
  • Kbd
  • Pagination
  • Progress
  • Separator
  • Skeleton
  • Spinner
  • Table
  • Tabs
  • Tree
Layout
  • Carousel
  • Resizable
  • ScrollArea
  • Sidebar
Dates
  • Calendar
  • DatePicker
  • DateRangePicker
  • TimePicker
Data
  • AreaChart
  • BarChart
  • Chart
  • DataTable
  • Kanban
  • LineChart
  • PieChart
  • RadarChart
  • RadialChart
  • VirtualList
Chat
  • Attachment
  • Message
  • MessageList
  • PromptInput
Elements UI / Forms / InputOTP

InputOTP

A one-time code in separate slots, with paste, SMS autofill and a pattern.

Autofocus

autofocus through attrs puts the cursor in the first slot when the page loads, the way a verification screen does.

Code
<InputOTP value={state.autofocus} maxLength={6} attrs={{ autofocus: true }} />

Usage

import { InputOTP, REGEXP_ONLY_DIGITS } from "@elements/ui/input-otp";

<InputOTP value={form.code} maxLength={6} groups={[3, 3]} pattern={REGEXP_ONLY_DIGITS} />
<InputOTP value={form.pin} maxLength={4} onComplete={(code) => verify(code)} />

Examples

Basic

Six slots in two groups. Type, paste, or let the phone fill the code from an SMS.

value: , completes: 0, changes: 0
Code
<InputOTP value={state.code} maxLength={6} groups={[3, 3]}
          onValueChange={() => state.changes++}
          onComplete={() => state.completes++} attrs={{ "aria-label": "Code" }} />

Digits only

pattern={REGEXP_ONLY_DIGITS} refuses anything else, typed or pasted.

empty
Code
<InputOTP value={state.digits} maxLength={6} pattern={REGEXP_ONLY_DIGITS} />

Separator

groups splits the slots; the separator slot replaces the dash.

Code
<InputOTP value={state.dots} maxLength={6} groups={[2, 2, 2]}>
  <span slot="separator" class="dot"></span>
</InputOTP>

Controlled

value is two-way: set it or clear it from code.

4
8
2
482
Code
<InputOTP value={state.code} maxLength={6} />
<Button onclick={() => state.code = ""}>Clear</Button>

Disabled, read-only and invalid

Disabled takes no input. readonly keeps the code focusable and submitted but refuses typing and paste. invalid draws the danger border on every slot.

1
2
3
4
8
1
0
0
0
0
0
0
Code
<InputOTP value={state.code} disabled />
<InputOTP value={state.code} readonly />
<InputOTP value={state.code} invalid />

Four digit PIN

maxLength={4} with a digits pattern and placeholder characters. A PIN is not a password manager's business, so pushPasswordManagerStrategy=none leaves the input at the slots' width. textAlign sets where the hidden input's text sits, which is where a long press lands; inputClass adds a class to the input.

0
0
0
0
Code
<InputOTP value={state.pin} maxLength={4} pattern={REGEXP_ONLY_DIGITS} placeholder="0000"
          pushPasswordManagerStrategy="none" textAlign="center" inputClass="pin-input" />

Letters and digits

REGEXP_ONLY_DIGITS_AND_CHARS with a text keyboard. pasteTransformer drops dashes and uppercases a pasted code.

empty
Code
<InputOTP value={state.ref} maxLength={6} inputmode="text"
          pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
          pasteTransformer={(t) => t.replace(/-/g, "").toUpperCase()} />

In a form

name submits the code; required blocks a partial one.

Verify your login

Enter the code we sent to your email.

not submitted
Code
<form onsubmit={(e) => verify(e)}>
  <Field label="Verification code">
    <InputOTP value={state.code} maxLength={6} groups={[3, 3]} name="code" required />
  </Field>
  <Button type="submit" variant="primary">Verify</Button>
</form>

Sizes

Slots are squares of the control height.

1
2
1
2
1
2
Code
<InputOTP value={state.code} maxLength={4} size="sm" />
<InputOTP value={state.code} maxLength={4} size="lg" />

Right to left

Under rtl the slots run right to left.

Code
<div dir="rtl"><InputOTP value={state.code} maxLength={6} groups={[3, 3]} /></div>
Reference InputOTP in the manual Every part, attribute, slot, key and data attribute.
Prev InputGroupNext Label
Product
Manual Pricing Compare
Company
History Contact
Legal
Privacy Policy Acceptable Use Terms License Agreement
© Elements Dev Corporation 2026