---
title: OTP input
seoTitle: OTP input field
order: 13
summary: A single input rendered as separate character slots, so a one-time password stays one real field for autofill, password managers and screen readers.
description: Add a one-time password (OTP) input with grouped slots, masking, and keyboard, paste and SMS autofill support.
related: [/ui/forms/elements, /ui/forms/validation]
source: core/scss/ui/forms/_form-otp.scss
css-vars: otp
classnames:
component:
- class: otp
desc: Wrapper; holds the real input and the rendered slots
part:
- class: otp-slots
desc: Container for the rendered, decorative slots
- class: otp-slot
desc: One character slot
- class: otp-separator
desc: Divider shown between groups
modifier:
- class: otp-connected
desc: Merges the slots into one bar with shared borders and no gap
size:
- class: otp-sm
desc: Smaller slots
- class: otp-lg
desc: Larger slots
---
import Example from '@components/Example.astro';
import CodeDocs from '@components/CodeDocs.astro';
## Overview
An OTP input looks like several boxes, but it is one real ``. JavaScript renders one `.otp-slot` per character on top of it and turns the field itself into a transparent overlay, so screen readers, password managers and SMS autofill still see a single ordinary text field.
Add `data-bs-toggle="otp"` to a wrapper with a text input inside, and Tabler renders the slots for you.
## Usage
### Connected slots
Add `.otp-connected` to merge the slots into one bar with shared borders and no gap between them.
### Groups and a separator
Use `data-bs-groups` to split the slots into visual groups, for example `[3,3]` for a "123 456" layout. `data-bs-separator` sets the character shown between groups; it defaults to `·`.
### Sizes
Add `.otp-sm` or `.otp-lg` for a smaller or larger control.
### Alphanumeric and masked
Set `data-bs-type` to `alphanumeric` or `alpha` to accept letters as well as digits. Add `data-bs-mask="true"` to show a mask character instead of the typed value - `getValue()` still returns the real value.
### Disabled
Add `disabled` to the inner input.
### Validation
Add `.is-valid` or `.is-invalid` to the `.otp` wrapper, the same way you would on a `.form-control`.
## Options
Options are read from `data-bs-*` attributes on the `.otp` element (the `data-tblr-*` alias also works), or passed to the constructor.
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `length` | number | `null` | Number of slots. When `null`, it is read from the input's `maxlength`, or `6`. |
| `type` | string | `'numeric'` | `numeric`, `alphanumeric`, or `alpha` - which characters each slot accepts. |
| `mask` | boolean | `false` | Shows a mask character in filled slots instead of the real value. |
| `groups` | array or null | `null` | Slot counts per visual group, for example `[3, 3]`. |
| `separator` | string | `'·'` | Character shown between groups. |
## JavaScript
Tabler creates an `OtpInput` for every element matching `[data-bs-toggle="otp"]` (or `[data-tblr-toggle="otp"]`):
Create one manually, or get an existing instance:
```javascript
const el = document.querySelector('.otp')
const otp = tabler.OtpInput.getOrCreateInstance(el, { length: 6, type: 'numeric' })
```
Listen for `complete.bs.otpInput` to know when every slot is filled:
```javascript
el.addEventListener('complete.bs.otpInput', (event) => {
console.log(event.value)
})
```
### Methods
| Method | Description |
| --- | --- |
| `getValue()` | Returns the current value. |
| `setValue(value)` | Sets the value and re-renders the slots. |
| `clear()` | Empties the value and focuses the input. |
| `focus()` | Focuses the input and selects the first empty slot. |
| `dispose()` | Removes the rendered slots and the instance. |
| `getInstance(element)` | Static. Returns the instance bound to the element, or `null`. |
| `getOrCreateInstance(element, config)` | Static. Returns the existing instance, or creates one. |
### Events
| Event | Description |
| --- | --- |
| `input.bs.otpInput` | Fires on every change from typing, backspace, paste, or autofill. `event.value` holds the current value. |
| `complete.bs.otpInput` | Fires once the value fills every slot, including from `setValue()`. `event.value` holds the full value. |
## Accessibility
- The visible slots are decoration (`aria-hidden`); a screen reader only ever sees the one real input, so label it like any other field with `aria-label` or a `