Tessina UI
ConfiguratorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleCheckboxChipCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFile Upload 2FlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

OTP Input

One-time password input with individual digit cells. Supports three visual variants, five sizes, five intents, masking, separator groups, numeric/alphanumeric/alphabetic input types, and full RTL/LTR support.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { OtpInput, OtpInputSeparator } from "@tessinaui/ui";
{/* Default — 6-digit numeric */}
<OtpInput length={6} onComplete={(code) => console.log(code)} />

{/* Controlled */}
<OtpInput length={6} value={code} onValueChange={setCode} />

{/* 4-digit PIN */}
<OtpInput length={4} />

{/* Masked (password-style) */}
<OtpInput length={6} mask />

{/* Filled variant */}
<OtpInput length={6} variant="filled" />

{/* Underline variant */}
<OtpInput length={6} variant="underline" rounded="none" />

{/* With separator — 3 + 3 groups */}
<OtpInput
  length={6}
  separator={<OtpInputSeparator />}
  separatorAfter={2}
/>

{/* Multiple separators */}
<OtpInput
  length={6}
  separator={<OtpInputSeparator>·</OtpInputSeparator>}
  separatorAfter={[1, 3]}
/>

{/* Alphanumeric */}
<OtpInput length={6} type="alphanumeric" />

{/* Error state */}
<OtpInput length={6} intent="error" />

{/* Auto-focus on mount */}
<OtpInput length={6} autoFocus />

{/* RTL */}
<OtpInput length={6} dir="rtl" />

Examples

Default

A basic 6-digit numeric OTP input with controlled value.

Variants

Three visual styles — outline, filled, and underline.

Sizes

Five cell sizes from xs to xl.

Intents

Colour intents for validation feedback — none, error, warning, success, and info.

Separator

Split the cells into groups with a separator after a given index.

States

Masked, disabled, and read-only inputs with preset values.

Variants

VariantDescription
outlineIndividual bordered cells — the classic OTP look
filledCells with a background fill; intent colours tint the fill
underlineBottom-border only for a minimal, form-field feel

Props

PropTypeDefaultDescription
lengthnumber6Number of input cells
valuestring—Controlled value
defaultValuestring""Uncontrolled initial value
onValueChange(value: string) => void—Called on every character change
onComplete(value: string) => void—Called when all cells are filled
variantoutline | filled | underlineoutlineVisual style
sizexs | sm | md | lg | xlmdCell size
intentnone | error | warning | success | infononeColour intent
roundednone | sm | md | lg | fullmdCell corner radius
typenumeric | alphanumeric | alphabeticnumericAllowed character set
maskbooleanfalseShow bullets instead of characters
separatorReactNode—Element rendered between groups
separatorAfternumber | number[]—Cell index (0-based) after which to insert the separator
placeholderstring—Character shown in empty cells (e.g. ·, ○, –)
disabledbooleanfalseDisables all cells
readOnlybooleanfalseCells are read-only
autoFocusbooleanfalseFocus the first empty cell on mount
dirltr | rtlltrText direction
aria-labelstring"One-time password"Accessible label for the group

Keyboard navigation

KeyAction
0–9 / A–ZEnter a character and advance to next cell
BackspaceClear current cell, or move back and clear previous
DeleteClear current cell without moving focus
← / →Move focus between cells
HomeFocus first cell
EndFocus last cell
PasteDistribute clipboard content across cells from the focused position

Accessibility

  • The group container uses role="group" with an aria-label
  • Each individual input has aria-label="Digit N"
  • autocomplete="one-time-code" is set automatically for numeric inputs
  • Focus is fully keyboard-navigable
  • Disabled state is communicated via disabled attribute on each input

NumberField

Numeric form input with stacked, split, inline or no increment/decrement buttons. Five sizes, five rounded values, full intent palette, three label positions, prefix/suffix slots, locale-aware formatting via Intl.NumberFormat, and LTR/RTL support.

Pagination

Navigate between pages of content. Five sizes, three active-page variants (default/primary/outline), five border-radius options (none/sm/md/lg/full), three nav styles (icon/label/none), smart ellipsis truncation, controlled/uncontrolled, and LTR/RTL support.

On this page

PlaygroundInstallationUsageExamplesDefaultVariantsSizesIntentsSeparatorStatesVariantsPropsKeyboard navigationAccessibility