A one-time code input with one slot per character.
import { OTPField } from '@oztix/roadie-components/otp-field'
Set length and OTPField renders that many slots. Typing moves to the next slot, Backspace moves back, and pasting a full code fills every slot at once. Codes are numeric by default, so phones show the number pad.
<Field> <Field.Label>Login code</Field.Label> <OTPField length={6} /> </Field>
groupSize splits the slots into groups with a separator between them. Six digits read as 3-3, which is easier to hold in your head while switching between apps.
<Field> <Field.Label>Login code</Field.Label> <OTPField length={6} groupSize={3} /> </Field>
normal is the sunken field surface that Input uses. subtle swaps the border for a tinted fill.
<div className='grid gap-4'> <OTPField length={6} aria-label='Normal' /> <OTPField length={6} aria-label='Subtle' emphasis='subtle' /> </div>
Slots match Input heights: sm is 32px, md 40px and lg 48px square, so they sit level with an Input or Button of the same size. Use lg for touch, such as checkout on a phone. When the row runs out of room, the slots shrink together rather than wrap.
<div className='grid gap-4'> <OTPField length={6} aria-label='Small' size='sm' /> <OTPField length={6} aria-label='Medium' size='md' /> <OTPField length={6} aria-label='Large' size='lg' /> </div>
Each slot uses is-interactive-field: neutral at rest, accent on focus, danger when invalid.
Wrap it in Field for the label, helper text and spacing. Field.Label names the first slot and the group, and the helper text describes the group.
<Field required> <Field.Label showIndicator>Verification code</Field.Label> <OTPField length={6} groupSize={3} /> <Field.HelperText>We sent a code to your email</Field.HelperText> </Field>
Set invalid on the Field. Every slot turns danger and the group points at the error text.
<Field invalid> <Field.Label>Verification code</Field.Label> <OTPField length={6} groupSize={3} defaultValue='204816' /> <Field.ErrorText>That code has expired</Field.ErrorText> </Field>
Pass OTPField.Input and OTPField.Separator as children to lay the slots out yourself. The number of inputs must match length.
onValueComplete fires once the last slot fills, whether the code was typed, pasted or autofilled. Use it to check the code straight away. autoSubmit submits the surrounding form instead.
<OTPField length={6} onValueComplete={verify} />
Do
Keep the default autoComplete='one-time-code' so phones offer the code from a text or email. Check it in onValueComplete so nobody has to find a button.
<OTPField length={6} autoComplete='off' />
Don’t
Don't turn autofill off or make people press Continue after the last digit.
<Field.ErrorText>That code has expired</Field.ErrorText>
Do
Name the problem, such as an expired or incorrect code, and offer a way to send a new one.
<Field.ErrorText>Invalid input</Field.ErrorText>
Don’t
Don't use a generic error. People can't tell if they mistyped or need a fresh code.
group. Field.Label names the group and the first slot, and the other slots are named by position, such as "Character 2 of 6". Outside Field, aria-label on the root does the same job.<input>, so screen readers, password managers and SMS autofill see it. A hidden input carries the whole value for forms.Field.HelperText or Field.ErrorText describes the group, and invalid sets aria-invalid on every slot.The id of the first input element. Subsequent inputs derive their ids from it (`{id}-2`, `{id}-3`, and so on).
The input autocomplete attribute. Applied to the first slot and hidden validation input.
Defaults to 'one-time-code'.
A string specifying the `form` element with which the hidden input is associated. This string's value must match the id of a `form` element in the same document.
The number of OTP input slots. Required so the root can clamp values, detect completion, and generate consistent validation markup before all slots hydrate.
Whether to submit the owning form when the OTP becomes complete.
Defaults to false.
Whether the slot inputs should mask entered characters. Pass `type` directly to individual `<OTPField.Input>` parts to use a custom input type.
Defaults to false.
The virtual keyboard hint applied to the slot inputs and hidden validation input. Built-in validation modes provide sensible defaults, but you can override them when needed.
The type of input validation to apply to the OTP value.
Defaults to 'numeric'.
Function that normalizes the OTP value after whitespace and `validationType` filtering. It runs whenever OTP Field normalizes a value, including initial/default values, controlled values, and user edits. The returned value is filtered by `validationType` again, then clamped to `length`. It should be idempotent because OTP Field may normalize the same value more than once while handling edits, storing state, and rendering controlled or uncontrolled values. Non-idempotent normalizers can compound across those normalization passes. Characters rejected while normalizing typed or pasted text are reported through `onValueInvalid`.
Whether the user must enter a value before submitting a form.
Defaults to false.
Whether the component should ignore user interaction.
Defaults to false.
Whether the user should be unable to change the field value.
Defaults to false.
Identifies the field when a form is submitted.
The OTP value.
The uncontrolled OTP value when the component is initially rendered.
Callback fired when the OTP value changes. The `eventDetails.reason` indicates what triggered the change: - `'input-change'` for typing or autofill - `'input-clear'` when a character is removed by text input - `'input-paste'` for paste interactions - `'keyboard'` for keyboard interactions that change the value
Callback fired when entered text contains characters that are rejected by validation or normalization before the OTP value updates. The `value` argument is the attempted user-entered string before normalization.
Callback function that is fired when the OTP value becomes complete, or when a complete value is pasted while the OTP is already complete. When the value changes, it runs later than `onValueChange`, after the internal value update is applied. If a complete pasted value matches the current value, `onValueChange` does not fire. If `autoSubmit` is enabled, it runs immediately before the owning form is submitted.
CSS class applied to the element, or a function that returns a class based on the component's state.
Slot size, matching `Input` heights: 32, 40 or 48px square. Use `lg` for touch.
Slot surface. `subtle` swaps the border for a tinted fill.
Marks the code as invalid. Inherits from `Field` when omitted.
Slots per group when the root renders its own slots. A separator sits between groups, so `length={6} groupSize={3}` reads as 3-3.
CSS class applied to the element, or a function that returns a class based on the component's state.
Slot size. Inherits from the root when omitted.
Slot surface. Inherits from the root when omitted.
CSS class applied to the element, or a function that returns a class based on the component's state.
Inherited from SeparatorProps
The orientation of the separator.
Defaults to 'horizontal'.