Forms

Input OTP

An accessible one-time password input with individual character slots and paste support.

Preview
01 Installation

Install

Pull Input OTP from the barrel for everyday use, or the granular path when you want a tighter bundle.

tsxBarrel1 line
import { InputOTP } from "@brika/clay";
tsxGranular1 line
import { InputOTP } from "@brika/clay/components/input-otp";
02 Usage

A minimal example

Drop this into a page. Native HTML attributes pass through to the underlying primitive.

tsxInput OTP.tsx26 lines
'use client';

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from '@brika/clay/components/input-otp';
/** Six-digit OTP split into two groups of three with a dash separator. */
export default function InputOTPDefaultDemo() {
  return (
    <InputOTP maxLength={6}>
      <InputOTPGroup>
        <InputOTPSlot index={0} />
        <InputOTPSlot index={1} />
        <InputOTPSlot index={2} />
      </InputOTPGroup>
      <InputOTPSeparator />
      <InputOTPGroup>
        <InputOTPSlot index={3} />
        <InputOTPSlot index={4} />
        <InputOTPSlot index={5} />
      </InputOTPGroup>
    </InputOTP>
  );
}
03 Examples

Input OTP, every way

Controlled

Controlled OTP that shows a Verify button only once all six slots are filled.

Numeric

Digits-only input using the REGEXP_ONLY_DIGITS pattern, rejects letters and symbols.

Pattern

Four-slot PIN entry without a separator, suitable for numeric PINs.

Placeholder

Muted dot placeholder in each empty slot, fades out on focus or fill.

04 Accessibility

Accessibility

  • Paste works out-of-the-box, pasting a code fills all slots.
  • A single hidden <input> handles the value; each visible slot is a presentation of that input's characters.
  • The active slot gets a focus ring matching the input's focus state.
  • Numeric-only patterns should use inputMode="numeric" to bring up the numeric keyboard on mobile.
05 Tokens 22 theme-overridable

Theme tokens

Every CSS variable Input OTP reads, with its default and the dotted path you'd write in a ThemeConfig JSON to override it. Set any of these in your theme to retune Input OTP without touching component code.

Input OTP tokens

22 tokens
ColorFill, border, and text colors.9
  • --input-otp-containercolorvar(--input-container)
    components.inputOtp.container

    Background color of each OTP slot in its resting state.

  • --input-otp-bordercolorvar(--input-border)
    components.inputOtp.border

    Border color of each OTP slot in its resting state.

  • --input-otp-labelcolorvar(--foreground)
    components.inputOtp.label

    Foreground color of the entered character rendered inside an OTP slot.

  • --input-otp-focus-bordercolorvar(--ring)
    components.inputOtp.focusBorder

    Border color of the active OTP slot (`data-active=true`).

  • --input-otp-focus-ringcolorvar(--ring)
    components.inputOtp.focusRing

    Color of the 3px focus halo painted around the active OTP slot. Rendered at 50% opacity to soften the outer glow.

  • --input-otp-invalid-ringcolorvar(--destructive)
    components.inputOtp.invalidRing

    Single color driving the invalid state on an OTP slot: the border uses it directly while the surrounding ring uses it at 20% opacity.

  • --input-otp-placeholder-colorcolorvar(--muted-foreground)
    components.inputOtp.placeholderColor

    Fill color of the placeholder dot rendered in an empty, non-focused OTP slot. Applied at 40% opacity in the rendered output.

  • --input-otp-caret-colorcolorvar(--foreground)
    components.inputOtp.caretColor

    Color of the blinking fake caret rendered inside the active OTP slot.

  • --input-otp-separator-colorcolorvar(--muted-foreground)
    components.inputOtp.separatorColor

    Color of the minus icon used as a visual separator between OTP groups.

GeometrySizes, lengths, and corner radii.2
  • --input-otp-sizesize2.5rem
    components.inputOtp.size

    OTP slot width and height.

  • --input-otp-radiusradiusvar(--radius-control)
    components.inputOtp.radius

    OTP slot corner radius.

BorderBorder width and style.2
  • --input-otp-border-widthborder-width1px
    components.inputOtp.borderWidth

    Border width on the input-otp. Set non-zero for outline-style variants.

  • --input-otp-border-styleborder-stylesolid
    components.inputOtp.borderStyle

    Border style on the input-otp (`solid`, `dashed`, `double`, `none`).

TypographyTypeface, size, weight, spacing.6
  • --input-otp-font-familyfont-familyvar(--font-sans)
    components.inputOtp.fontFamily

    Typeface for input-otp.

  • --input-otp-font-sizefont-sizevar(--text-body-md)
    components.inputOtp.fontSize

    Font size for input-otp.

  • --input-otp-font-weightfont-weight500
    components.inputOtp.fontWeight

    Font weight for input-otp.

  • --input-otp-line-heightline-height1.25
    components.inputOtp.lineHeight

    Line height for input-otp.

  • --input-otp-letter-spacingletter-spacing0
    components.inputOtp.letterSpacing

    Letter spacing for input-otp. Useful for caps labels.

  • --input-otp-text-transformtext-transformnone
    components.inputOtp.textTransform

    Text transform for input-otp (`uppercase`, `lowercase`, `capitalize`, `none`).

ElevationDrop shadow and depth.1
  • --input-otp-backdrop-blurblur0px
    components.inputOtp.backdropBlur

    Backdrop blur applied to the input-otp slot surface. Set non-zero for a frosted-glass treatment.

MotionAnimation duration and easing.2
  • --input-otp-durationdurationvar(--motion-standard-duration)
    components.inputOtp.duration

    Transition duration for input-otp state changes.

  • --input-otp-easingeasingvar(--motion-standard-easing)
    components.inputOtp.easing

    Transition easing for input-otp state changes.

Override in a theme

Authoring a theme is plain JSON. Drop overrides under components.inputOtp, the names are camelCase versions of the variable suffix.

jsonmy-theme.json12 lines
{
  "id": "my-theme",
  "name": "My Theme",
  "description": "...",
  "accentSwatches": ["#000"],

  "components": {
    "inputOtp": {
      "radius": "var(--radius-control)"
    }
  }
}

Then apply with applyTheme(myTheme) (see the theming guide), scope it to a subtree with themeToCssVars(myTheme, mode), or consume any token directly in your own JSX with the matching Tailwind utility (e.g. bg-input-otp-…) — see the chips on each token row.

06 API reference

API reference

Props specific to Input OTP. Native HTML attributes pass through to the underlying primitive, see the component source on GitHub for the full type signature.

<InputOTP />

1 prop

<InputOTPGroup />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<InputOTPSeparator />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<InputOTPSlot />

2 props
  • number

    No description.

  • placeholderdefault false
    boolean
    Render a muted dot in the centre of the slot while it's empty and not focused. Disappears as soon as the slot has a value or becomes the active slot (where the caret takes over).