Skip to content

Input OTP

Copy editable source into an initialized application:

Terminal window
pnpm dlx @simurgh-ui/cli add input-otp

For package consumption, import from the component subpath and load its optional recipe CSS:

import { InputOtp } from '@simurgh-ui/react/input-otp';
import '@simurgh-ui/styles/input-otp.css';
import { InputOtp } from '@simurgh-ui/vue/input-otp';
import '@simurgh-ui/styles/input-otp.css';
import { InputOtpComponent } from '@simurgh-ui/angular/input-otp';
import '@simurgh-ui/styles/input-otp.css';

Omit the component stylesheet for fully headless styling. CLI-copied components use the local path written to simurgh.json and the copied application styles instead of these package imports.

Input OTP uses one native input so password managers, SMS code autofill, selection, and paste continue to work. The shared recipe presents the value as evenly spaced code cells without splitting keyboard focus across multiple controls.

The optional component stylesheet provides a complete default recipe, including visual hierarchy and interaction states. Consumers can override semantic tokens or omit the recipe.

Live component Input OTP
Enter the six-digit code.

The public component parts are listed in API surface below. Use only the parts needed by the example; compound components depend on their documented parent/child nesting.

Controlled state remains the application’s source of truth and must be updated from every change event. Uncontrolled state reads its default only when the component mounts. Do not pass both forms of the same state at once.

FrameworkExact state contract and reset behavior
ReactNo public controlled/uncontrolled state pair is exposed.
VueControlled: v-model (modelValue + update:modelValue). No uncontrolled prop is exposed. Reset the model ref to its initial value; reset uncontrolled state with a changed Vue :key.
AngularControlled two-way binding: [(value)] (value + valueChange). Angular exposes no separate default input; initialize and reset the bound class field explicitly.

Vue

<script setup lang="ts">
import { ref } from 'vue';
const initial = '';
const state = ref(initial);
const resetKey = ref(0);
</script>
<InputOtp v-model="state"></InputOtp>
<button @click="state = initial">Reset controlled</button>

Angular

initial = '';
value = this.initial;
reset() { this.value = this.initial; }
<simurgh-input-otp [(value)]="value"></simurgh-input-otp>
<button type="button" (click)="reset()">Reset</button>

The default digit-only mode removes non-numeric pasted characters and limits the value to length. Disable digitsOnly for alphanumeric recovery codes. Always provide a visible label and avoid masking one-time codes unless the threat model specifically requires it.

<Label htmlFor="code">Verification code</Label>
<InputOtp id="code" name="code" length={6} required />

Preserve the documented composition and accessible names when wrapping or restyling this component. See accessibility and RTL guidance for keyboard, focus, labeling, and directionality requirements.

  • React exports: InputOtp
  • Vue exports: InputOtp
  • Angular exports: InputOtpComponent

Import these public symbols from the component subpath shown in Installation. Framework-specific props, events, slots, directives, methods, defaults, and native-attribute behavior belong in the API tables on this page; use the linked source only to verify the current implementation.

React API reference

An inherited-attributes entry means the component accepts the complete named React native interface, including its event handlers and ARIA and data attributes. Remaining attributes are forwarded to the rendered element unless the component behavior described on this page overrides them.

Inherited attributes: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement>.

PropTypeDefault / requirement
childrenReactNodeundefined
digitsOnlybooleantrue
invalidbooleanfalse
lengthnumber6
refRef<HTMLInputElement> | undefinedundefined
Vue API reference

Boolean props without explicit defaults use Vue’s false default. Undeclared attributes follow the fallthrough behavior stated for each component.

Vue attribute fallthrough is enabled for the rendered root.

PropTypeDefault / requirement
modelValuestring''
lengthnumber6
digitsOnlybooleantrue
namestringundefined
requiredbooleanfalse
disabledbooleanfalse
invalidbooleanfalse
autocompletestring'one-time-code'
EventPayload
update:modelValuestring
changeEvent

Slots: none.

Exposed methods: none.

Angular API reference

Inputs and outputs use their public template names. Public methods are callable through a template reference or ViewChild. Native attributes apply to the documented template root or directive host; they are not automatically forwarded through component hosts.

Component selector: simurgh-input-otp. The template’s first native element is input.

InputTypeDefault / requirement
namestringundefined
valuestring''
lengthnumber6
digitsOnlybooleantrue
requiredbooleanfalse
disabledbooleanfalse
invalidbooleanfalse
autocompletestring'one-time-code'
OutputPayload
valueChangestring

Content projection: none.

Public method
update(event: Event): void

Follow the framework example as a structural contract. Root components own shared state; parts that consume that state must stay under the root (or be attached to projected descendants in Angular). Parts described as conditional are optional until their corresponding interaction or semantic region is used. Do not render a state-consuming part by itself.

FrameworkRequired parent/child relationshipConditional partsSupporting types
ReactInputOtp is standalone and required when using this component.Content is optional unless the API requires a label or value.None.
VueInputOtp is standalone and required when using this component.Content is optional unless the API requires a label or value.None.
AngularInputOtpComponent is standalone and required when using this component.Content is optional unless the API requires a label or value.None.

Accessible names, descriptions, and form labels remain required whenever the component’s purpose cannot otherwise be determined, even when the corresponding visual part is optional.

The adapters target the same user-visible behavior and accessibility contract. Their public shapes follow each framework’s conventions and are not expected to be symbol-for-symbol identical.

ConcernContract
Public compositionAll adapters export 1 public symbol, with framework-idiomatic names.
State and change eventsReact uses controlled/default props and callbacks; Vue uses v-model; Angular uses [(value)].
Children and contentReact uses children; Vue uses the slots listed above; Angular uses the documented content projection selectors.
Native attributesReact forwards the named native interface; Vue follows the stated fallthrough rule; Angular attributes apply to the component host unless an input, directive, or documented native root consumes them.
Imperative accessReact forwards native refs; Vue exposes no methods; Angular exposes the listed class methods through a template reference or ViewChild.

These differences are intentional adapter design. Behavioral or accessibility differences not stated on this page are parity defects rather than supported variations.

Use name="verificationCode" to serialize the current value under the documented field name. The examples initialize the field, keep its value application-owned, forward disabled and required, render an accessible error, and read the browser submission payload. Set invalid when validation fails; it communicates visual/ARIA state but does not replace an error message.

const initial = '';
const [value, setValue] = useState(initial);
const [errors, setErrors] = useState<Record<string, string>>({});
<form onSubmit={(event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
// Submit data.get('verificationCode').
}}>
<InputOtp name="verificationCode" value={value} onChange={(event) => setValue(event.currentTarget.value)}
required disabled={isDisabled} invalid={Boolean(errors.verificationCode)} />
{errors.verificationCode && <p role="alert">{errors.verificationCode}</p>}
<button type="submit">Submit</button>
</form>
<script setup lang="ts">
import { reactive, ref } from 'vue';
const initial = '';
const value = ref(initial);
const errors = reactive<Record<string, string>>({});
function submit(event: Event) {
const data = new FormData(event.currentTarget as HTMLFormElement);
// Submit data.get('verificationCode').
}
</script>
<form @submit.prevent="submit">
<InputOtp v-model="value" name="verificationCode" required :disabled="isDisabled" :invalid="Boolean(errors.verificationCode)"></InputOtp>
<p v-if="errors.verificationCode" role="alert">{{ errors.verificationCode }}</p>
<button type="submit">Submit</button>
</form>
initial = '';
value = this.initial;
errors: Record<string, string> = {};
submit(form: HTMLFormElement) {
const data = new FormData(form);
// Submit data.get('verificationCode').
}
<form #form (submit)="submit(form); $event.preventDefault()">
<simurgh-input-otp [(value)]="value" name="verificationCode" required [disabled]="isDisabled" [invalid]="!!errors.verificationCode"></simurgh-input-otp>
<p *ngIf="errors.verificationCode" role="alert">{{ errors.verificationCode }}</p>
<button type="submit">Submit</button>
</form>

For Angular reactive or template-driven forms, bridge the documented value/valueChange pair to your form control. The component does not implement ControlValueAccessor; update disabled and validation state from the Angular form explicitly.

The table distinguishes component behavior from application-owned presentation. “Not supported” means there is no public state contract for that adapter; do not invent one with an undocumented attribute. Keep status and error messages accessible when they are rendered outside the component.

StateReactVueAngular
LoadingNot supported by this component API; handle this state in surrounding application UI.Not supported by this component API; handle this state in surrounding application UI.Not supported by this component API; handle this state in surrounding application UI.
EmptyNot supported by this component API; handle this state in surrounding application UI.Not supported by this component API; handle this state in surrounding application UI.Not supported by this component API; handle this state in surrounding application UI.
InvalidSupported with invalid={true}; also associate visible error text.Supported with :invalid="true"; also associate visible error text.Supported with [invalid]="true"; also associate visible error text.
Read-onlySupported with readOnly={true}; focus and value submission remain available while editing is blocked.Not supported by this component API; handle this state in surrounding application UI.Not supported by this component API; handle this state in surrounding application UI.
DisabledSupported with disabled={true} on the documented control or interactive part; its interaction is blocked. Disabled form controls are omitted from submission.Supported with :disabled="true" on the documented control or interactive part; its interaction is blocked. Disabled form controls are omitted from submission.Supported with [disabled]="true" on the documented control or interactive part; its interaction is blocked. Disabled form controls are omitted from submission.
ErrorSet the invalid state and render a separate labeled error message; invalid styling alone is not an error description.Set the invalid state and render a separate labeled error message; invalid styling alone is not an error description.Set the invalid state and render a separate labeled error message; invalid styling alone is not an error description.
Styling contract

The selectors below are used by the published component recipe or emitted consistently by an adapter. Treat these as the supported styling surface. Element order, anonymous wrappers, and undocumented descendants are implementation details and should not be targeted.

SurfaceStable hooks
Recipe classes.simurgh-trigger
Stable DOM parts[data-slot="button"], [data-slot="checkbox"], [data-slot="input"], [data-slot="input-otp"], [data-slot="native-select"], [data-slot="pagination-link"], [data-slot="select-trigger"], [data-slot="toggle-group-item"], [data-slot="toolbar-button"]
Stable data attributes[data-density], [data-slot]
ARIA/state selectors used by the recipe[aria-invalid]
CSS custom properties consumed--simurgh-border, --simurgh-control-height, --simurgh-control-padding, --simurgh-danger, --simurgh-foreground, --simurgh-otp-length, --simurgh-radius, --simurgh-ring, --simurgh-surface

Prefer a semantic token override for theme-wide changes. Use the listed class or part selectors for a component-scoped override. When omitting recipe CSS, preserve state attributes and ARIA semantics even if your replacement styles use different selectors.

Load the optional component stylesheet for the default recipe, override semantic tokens for broad theme changes, or omit the recipe CSS for headless styling. See theming and styling hooks for import order, selectors, dark mode, RTL, and reduced-motion guidance.

Last verified on 2026-08-13 against Simurgh registry 0.1.1.