Skip to content

CdsOtpInput

New in v4.0

A field for entering a one-time code — from an SMS, an email or an authenticator app. It looks like a row of separate cells, but the input is taken by a single hidden input: that is why the SMS autofill, pasting a whole string from the clipboard, selection and the arrow keys all work, and the focus does not have to be jumped between the cells by hand.

Storybook GitLab
Test coverage: 93% — good coverage
API in specs
44 / 44 props, events, slots and exposed members with a test
Test cases
78 unit · 3 browser
Known bugs
5 pinned by an it.fails test
Code coverage
93.4% lines · 85.6% branches measured in v4.0.0-alpha.13
A11y (axe)
3 recommendations in 13 stories measured in v4.0.0-alpha.11

For an ordinary short text or number take CdsTextInput: CdsOtpInput is needed where the length of the input is known upfront and every character is shown in its own cell.

Anatomy ​

The cells (CdsOtpField) only draw the code — the entered character, the placeholder or the blinking caret. The real input is taken by a hidden input: it is pressed to the bottom edge of the component, so the SMS autofill suggestion appears under the cells and covers nothing. A separator (CdsOtpSeparator) may stand between the cells, and in the merged look the cells are gathered into a group (CdsOtpGroup).

Variations ​

Length and type of the code ​

The length prop sets the number of cells, type — what may be entered: number (the default) accepts digits only and opens the numeric keyboard on mobile, text — any characters, password additionally hides what is entered behind dots. The same effect without changing the type is given by masked.

Allowed characters ​

The pattern prop narrows the set of characters: the name of a ready set (numeric, alpha, alphanumeric, unicode-alpha, unicode-alphanumeric) or your own regular expression. The check goes character by character — the extra ones will not get into the field, neither from the keyboard nor from the clipboard.

Separator ​

The divider prop puts a character between the cells — a hyphen in a code like 123-456, for example. The divider slot gives full freedom of markup and receives index — the number of the cell to the left of the separator, so the separator can be placed only where it is needed.

Merged look ​

The merged prop removes the gaps: the cells become a single field with a common border. This look is more compact and breaks the form up less.

Placeholder ​

The placeholder prop shows a character in the empty cells — a hint about the format or simply a dot, so that the row does not look empty.

Sizes ​

The size prop changes the height of the cells and the font size. The default size is md.

States ​

disabled blocks the input, error highlights the whole row with the error color, loading shows an indicator on top of the cells and does not erase the entered code. focus-all highlights the whole row while the field is focused, not just the active cell.

Slots ​

SlotDescription
fieldsThe whole row of cells — your own markup of CdsOtpGroup, CdsOtpField and CdsOtpSeparator
dividerThe separator between the cells, index is the number of the cell to its left
loaderYour own loading indicator (with loading)
defaultArbitrary content on top of the cells

The fields slot is needed when the cells are gathered into groups: every CdsOtpField receives its own index, and CdsOtpGroup joins the neighboring cells into a common border.

Guidelines ​

Scenarios ​

Confirmation by SMS ​

The finish event fires as soon as the whole code is entered — send the request on it, a separate button is not needed. Turn on loading for the duration of the check: the code stays on the screen and the field does not look broken. If the server did not accept the code, error highlights the row, and the reset() and focus() methods called through a ref clear the field and bring the cursor back into it.

Resending the code ​

The "Send again" button opens on a timer — that way the user does not flood the gateway with requests. Reset the field together with the new code: reset() clears the cells, focus() puts the cursor into the first one, and the old code does not mix with the new one.

A code inside a form ​

When the code is a part of the sign-in form, keep the confirmation button: it gives an explicit moment of submission. The button waits until all the characters are entered — the length of the code is visible from v-model, and disabled on the field freezes the code that has already been confirmed.

API ​

CdsOtpInput ​

Props

NameTypeDefaultDescription
activeColorstring | undefined—Color of the component in its active state
autofocusboolean | undefined—Enables autofocus
baseColorstring | undefined—Color of the component in its inactive state
bgColorstring | undefined—Background color of the component
colorstring | undefined—Component color
disabledboolean | null | undefinednullDisabled state
dividerstring | undefined—Separator character placed between the code cells
errorboolean | undefined—Error state
focusAllboolean | undefined—Highlight all the cells while the field is focused, not only the active one
focusedboolean | undefined—Controls the focus state of the component. When true, the component is considered focused and may apply the corresponding styles or behavior.
heightstring | number | undefined—Component height
labelstring | undefined'$cds.input.otp'Accessible name of the field (aria-label). Accepts a localization key such as $cds.input.otp
lengthstring | number | undefined6Number of code cells
loadingstring | boolean | undefined—Loading indicator for the component
maskedboolean | undefined—Hide the entered characters behind dots
maxHeightstring | number | undefined—Maximum component height
maxWidthstring | number | undefined—Maximum component width
mergedboolean | undefined—Merged look: the cells are joined into a single field with no gaps between them
minHeightstring | number | undefined—Minimum component height
minWidthstring | number | undefined—Minimum component width
modelValuestring | number | undefined—Value of the component (v-model)
patternRegExp | "numeric" | "alpha" | "alphanumeric" | "unicode-alpha" | "unicode-alphanumeric" | undefined—Allowed characters: the name of a ready pattern (numeric, alpha, alphanumeric, unicode-alpha, unicode-alphanumeric) or your own regular expression
placeholderstring | undefined—Placeholder character shown in an empty cell
sizenumber | "xs" | "sm" | "md" | "lg" | "xl" | undefined'md'Size of the component
textColorstring | undefined—Text color of the component
themestring | undefined—Component theme
typeCdsOtpInputType | undefined'number'Type of the code being entered. number limits the input to digits, password hides the characters
widthstring | number | undefined—Component width

Events

NameTypeDescription
finish[value: string]Fires when the whole code has been entered
update:focused[focused: boolean]Fires when the field gains or loses focus
update:modelValue[value: string]Fires when the value of the component changes

Slots

NameTypeDescription
defaultanyArbitrary content on top of the code cells
divider{ index: number; }Separator between the cells. index is the number of the cell to the left of it
fieldsanyFully replaces the row of cells with your own markup made of CdsOtpGroup, CdsOtpField and CdsOtpSeparator
loaderanyCustom loading indicator

Exposed

NameTypeDescription
blur() => voidRemove the focus from the field
focus() => voidSet the focus on the field
inputRefHTMLInputElement | undefinedRef to the hidden input that receives the code
isFocusedboolean | undefinedFocus state of the field
reset() => voidClear the entered code

CdsOtpField ​

Props

NameTypeDefaultDescription
index*number—Zero-based index of the cell

CdsOtpGroup ​

Props

NameTypeDefaultDescription
mergedboolean | null | undefinednullMerged look of the group. Inherited from the parent CdsOtpInput by default

Slots

NameTypeDescription
defaultanyThe code cells of the group

CdsOtpSeparator ​

Slots

NameTypeDescription
defaultanyContent of the separator