OTP input

A six digit verification flow with paste support and keyboard navigation.

Preview
OTP input preview
Verification code

When to use

  • Verification codes from email or SMS.
  • Two-factor authentication code entry.

When not to use

  • Use password-field for secrets people type from memory.
  • Use input for any other text.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "otp-input",
  "type": "registry:block",
  "title": "OTP input",
  "description": "A six digit verification flow with paste support and keyboard navigation.",
  "categories": [
    "blocks"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/otp-input/otp-input.tsx",
      "type": "registry:component",
      "target": "@components/manicat/otp-input/otp-input.tsx"
    },
    {
      "path": "registry/components/otp-input/otp-input.module.css",
      "type": "registry:component",
      "target": "@components/manicat/otp-input/otp-input.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/blocks/otp-input",
  "meta": {
    "tier": "free",
    "kind": "block",
    "docs": "https://manicat.viraza.net/components/blocks/otp-input",
    "markdown": "https://uiarc.dev/components/blocks/otp-input/markdown"
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { useState } from "react";
import { OtpInput } from "@/components/manicat/otp-input/otp-input";

export function OtpInputExample() {
  const [value, setValue] = useState("");

  return <OtpInput label="Verification code" length={6} value={value} onChange={setValue} />;
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Verify your email Six digits, auto-advancing, with paste support, then a confirm button.

Verify your email Six digits, auto-advancing, with paste support, then a confirm button. preview
Verification code

Keyboard interactions

KeysAction
0-9 / charactersFills the slot and moves to the next one.
ArrowLeft / ArrowRightMoves between slots.
BackspaceOn an empty slot, clears the previous one and moves back.
DeleteClears the current slot.
Cmd/Ctrl + VPastes a code across the slots from the focused one.

Accessibility

  • Slots sit in a labelled role=group, each with an aria-label like Verification code, digit 1 of 6.
  • Helper and error text are linked with aria-describedby; the error uses role=alert and sets aria-invalid.
  • The first slot uses autocomplete one-time-code so browsers can offer SMS codes.

Motion

  • Typed characters rise into the slot and unblur; a paste lands as a short left-to-right wave.
  • One focus ring glides between slots, and a new error nudges the row side to side once.
  • Reduced motion removes the glide, shake, and blur and uses instant fades.

Responsive behavior

  • Slots are 42px wide and can shrink with max-width 100%; below 360px gaps tighten and slots drop to 44px tall.
  • The first slot uses autocomplete one-time-code, so iOS and Android can offer SMS codes.
  • numeric inputMode brings up the number pad on phones.

Performance

  • Only a handful of inputs plus one gliding ring; a ResizeObserver springs the message height.

Notes for AI

  • Use for verification and two-factor codes. Use input or password-field for anything else.
  • It is controlled: keep value in state and submit when value.length equals length. There is no default export, so import it by name.