Data input

Input OTP

A one-time code field: one native text input shown as a row of digit slots.

Class nameTypeDescription
input-otpplf:input-otpComponentHolds the input and its slots
input-otp-inputplf:input-otp-inputPartThe native text input, beside input
input-otp-slotsplf:input-otp-slotsPartThe slot row the script adds
input-otp-groupplf:input-otp-groupPartSlots the script joins into one box
input-otp-slotplf:input-otp-slotPartOne digit's box
input-otp-separatorplf:input-otp-separatorPartThe mark the script adds between groups

Default

The script draws one slot per digit, from maxlength, so without JavaScript the field is an ordinary Input.

Paste a code or edit it with the keyboard.

Separator

data-plf-groups splits the slots into groups, with a separator between them.

Paste a code or edit it with the keyboard.

Length

Set maxlength to 4 or 6; a group of 1 is a separate box.

Paste a code or edit it with the keyboard.

Paste a code or edit it with the keyboard.

Disabled

A disabled fieldset or aria-disabled="true" blocks editing as disabled does.

Paste a code or edit it with the keyboard.

Read-only

A read-only code can still be focused, selected and copied.

Paste a code or edit it with the keyboard.

Invalid

Set aria-invalid="true" on the input, not the container, and link the error with aria-describedby.

This code has expired. Request a new code.

Verification form

Checking the code and sending a new one stay with your server. PLF.Field.error() shows your message under the field until the next edit; this example’s preview accepts 123456.

Verify your email

Enter the six-digit code for m@example.com.

Six digits. You can paste the entire code.

JavaScript

Input OTP needs JavaScript. The Full script (pelatform-ui.min.js) starts every component on page load, with no code of your own; with ESM imports, call init() yourself, as in Installation. For markup you add later, see Dynamic content.

<link rel="stylesheet" href="pelatform-ui.min.css">
<script src="pelatform-ui.min.js" defer></script>
<link rel="stylesheet" href="pelatform-ui.prefixed.min.css">
<script src="pelatform-ui.min.js" defer></script>
Attribute Purpose
data-plf-input-otp The container, holding one <input type="text"> with maxlength="4" or "6"
data-plf-groups On the container: group sizes separated by spaces that add up to maxlength, such as 3 3; a group of 1 is a separate box. One group of every digit by default
data-plf-input-otp-active Set by the script on the active slot
inputmode="numeric", spellcheck="false", pattern Added to the input when missing; the script also sets dir="ltr", so digits stay left to right in right-to-left layouts
autocomplete="one-time-code" Added when missing; only a hint, since whether a phone or password manager offers the code depends on the browser and device
Method Behavior
PLF.InputOTP.getOrCreateInstance(element) Returns the instance, creating it for valid markup; invalid markup logs an error. Calling it again keeps the instance and its value
PLF.InputOTP.getInstance(element) Returns the instance or null
instance.getValue() Returns the digits entered
instance.getState() Returns {value, length, filledCount, complete, disabled, readOnly}
instance.setValue(value) Keeps the digits of value, up to the length; returns false when nothing changed, the change was canceled or the input is disabled or read-only
instance.clear() Empties the field; returns false in the same cases
instance.focus(index) Focuses the input with the caret at index, or after the last digit; returns false when the caret is already there or the input is disabled or hidden
instance.destroy() Removes listeners, the slots and the attributes the script added; the value stays. Destroy and initialize again to change the length or groups

With ESM, import InputOTP from @pelatform/ui/components/input-otp.

After setting input.value in code, dispatch an input event, or use setValue(), so the slots update.

Event When
plf:input-otp:before-change Before a new value is accepted; cancelable
plf:input-otp:change After the value changed
plf:input-otp:complete After a change that fills every slot; the form is not submitted

Each event’s detail:

Property Events Value
instance All The Input OTP instance
reason All api, or trigger for typing and pasting
trigger All The input, or null from the API
state All The new state, shaped like getState()
previousState before-change, change The state before the change

Events bubble from the container. Canceling before-change restores the previous value and selection. Initialization, a native form reset (which empties the slots) and destroy() send no events.

Keyboard

Key Action
Tab, Shift+Tab Moves to and from the field; it is one tab stop
Digits Enter a digit at the caret; other characters are dropped
Arrow keys, Home, End Move the caret, natively
Backspace, Delete Remove digits, natively
Enter Submits the form, natively
Click on a filled slot Selects its digit for replacement

React

InputOTP from @pelatform/react renders the input with the slots the script draws, and takes typing, pasting and the selection as the script does; inside a Field the input takes the field’s label, help and error.

Prop Values Default
maxLength 4 or 6, the number of digits and slots Required
groups Group sizes that add up to maxLength, such as [3, 3], with a separator between groups; a group of 1 is a separate box One group
value The digits, which the application changes in onValueChange; keeping them refuses the edit, as canceling before-change does Its own state
defaultValue The digits it starts with when the application does not set value Empty
onValueChange (value), called with the digits when typing or pasting changes them None
onComplete (value), called after a change that fills every slot; the form is not submitted None
containerClassName Classes for the input-otp container, such as w-full, as className goes to the input None

The other props are the native input’s, className and ref included; it adds inputMode, autoComplete, spellCheck and pattern as the script does.

CSS variables

Override these variables on a root or on a single Input OTP.

Variable Default
--plf-input-otp-slot-size --plf-control-height-md, 2.25rem (36px), the slot width and height; slots shrink to fit a narrow container
--plf-input-otp-gap Space 2, 0.5rem (8px), between groups or separate slots
--plf-input-otp-font-size Large font size, 1.125rem (18px)
--plf-input-otp-bg, --plf-input-otp-color --plf-input-bg, else the field color; text
--plf-input-otp-border --plf-input-border, else --plf-color-input
--plf-input-otp-radius Large radius, 0.5rem (8px)
--plf-input-otp-focus-color --plf-focus-color, the active slot’s outline
--plf-input-otp-invalid-border --plf-color-danger
--plf-input-shadow, --plf-input-font-weight Shared with other fields

There is no pattern option for letters and no masked PIN mode.


Built with Pelatform UIv0.1.0

Components

Getting started

Foundations

Components

Actions

Data display

Navigation

Feedback

Data input

Layout

Advanced

Integrations

Blocks

Application

Authentication

Icons