輸入元件

OTP Input

驗證碼分格輸入:單一隱形 input 蓋在視覺格子上,支援貼上、錯誤抖動、成功波紋泛綠。

把這個效果裝進你的專案

在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。

npx webberui@latest add otp-input

還沒有專案,或安裝遇到問題?看快速開始 →

OTP Input 是用 React、Tailwind CSS 與 Motion 做的一次性密碼(OTP)/簡訊驗證碼輸入框:畫面上是分開的格子,底下其實只有一個原生 input,所以貼上、自動填入與刪除都照瀏覽器原本的行為運作。

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

6
編輯中的呼叫預設屬性
<OTPInput/>

完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。

適用情境

  • 登入或註冊流程的簡訊、Email 驗證碼確認頁
  • 兩步驟驗證(2FA)輸入驗證器 App 產生的 6 位數代碼
  • 付款或修改密碼前的二次確認
  • 輸入英數混合的邀請碼、兌換碼(搭配 type="alphanumeric")
  • 不適合:長度不固定的優惠碼,改用 Coupon Code Input

安裝

npx webberui@latest add https://webberui.com/r/otp-input.json

安裝時會一併寫入 --wb-duration-fast、--wb-ease-out CSS 變數到你的全域樣式。

使用

import { OTPInput } from "@/components/ui/otp-input";

<OTPInput
  onComplete={(code) => verify(code)}
  error={isError}
  success={isSuccess}
/>

不傳 value 時由元件內部管理;需要在驗證失敗後清空重來時,改用受控模式(value + onChange)。

範例

驗證失敗後清空重來

用受控模式接 API:驗證成功時亮綠,失敗時先抖動,0.6 秒後清空讓使用者重新輸入。

"use client";

import * as React from "react";
import { OTPInput } from "@/components/ui/otp-input";

export function VerifyCodeForm() {
  const [code, setCode] = React.useState("");
  const [error, setError] = React.useState(false);
  const [success, setSuccess] = React.useState(false);

  async function handleComplete(value: string) {
    const res = await fetch("/api/verify-code", {
      method: "POST",
      body: JSON.stringify({ code: value }),
    });
    if (res.ok) {
      setSuccess(true);
      return;
    }
    setError(true);
    setTimeout(() => {
      setCode("");
      setError(false);
    }, 600);
  }

  return (
    <OTPInput
      value={code}
      onChange={(next) => {
        setCode(next);
        setError(false);
      }}
      onComplete={handleComplete}
      error={error}
      success={success}
    />
  );
}

英數混合的 8 碼邀請碼

type="alphanumeric" 會過濾符號並自動轉大寫,length 改成 8 格。

<OTPInput
  length={8}
  type="alphanumeric"
  onComplete={(code) => redeemInvite(code)}
/>

送出期間停用

驗證請求進行中時傳入 disabled,避免重複送出;className 會套在外層容器上,可用來調整對齊。

<OTPInput
  length={4}
  disabled={isVerifying}
  onComplete={(code) => startVerify(code)}
  className="mx-auto"
/>

Props

Prop型別預設值說明
lengthnumber6格數
valuestring—受控值;不傳則由元件內部管理
onChange(value: string) => void—值變更時觸發
onComplete(code: string) => void—輸入滿 length 位時觸發,回傳完整驗證碼
errorbooleanfalse錯誤狀態:整排 x 軸抖動、邊框轉紅
successbooleanfalse成功狀態:格子依序波紋泛綠
type'numeric' | 'alphanumeric''numeric'允許的字元類型;alphanumeric 會自動轉大寫
disabledbooleanfalse停用輸入

可及性

採「單一隱形 input + 視覺格子」模式,而非 N 個獨立 input:

  • 螢幕閱讀器只會看到一個標準文字欄位(aria-label="驗證碼"),不用在六個欄位間跳轉;視覺格子皆為 aria-hidden
  • autocomplete="one-time-code":iOS/Android 收到簡訊驗證碼時可一鍵自動填入
  • 貼上完整驗證碼直接生效,無需逐格分配
  • 鍵盤行為完全是原生 input:Backspace 刪除、inputMode="numeric" 在行動裝置叫出數字鍵盤
  • error 時設定 aria-invalid,輔助技術可得知欄位狀態

細節

  • 當前格顯示呼吸明滅的 caret 與 focus ring,字元以 scale 0.5 → 1 pop 進格
  • 成功波紋以每格 60ms 的 stagger 依序將邊框與文字轉綠
  • 使用者系統開啟「減少動態效果」時,停用抖動與 pop 動畫,字元與狀態直接顯示

常見問題

可以在 Next.js App Router 使用嗎?

可以。元件檔案開頭已經有 "use client",在 Server Component 頁面裡照一般方式 import 即可。

支援手機簡訊自動填入嗎?

支援。隱形 input 設定了 autocomplete="one-time-code",iOS 與 Android 收到簡訊時可以一鍵帶入;直接貼上整串驗證碼也會一次填滿,非數字字元會被濾掉。

受控模式下 onComplete 會觸發嗎?

會。只要值的長度從不足 length 變成等於 length 就會觸發,不論是使用者輸入,還是你從外部把 value 填滿。

怎麼改格子的顏色和尺寸?

格子的尺寸(h-12 w-10)、邊框與錯誤/成功色都是寫在原始碼裡的 Tailwind class,直接修改 otp-input.tsx 即可;className 只作用在外層容器。

有考慮減少動態效果嗎?

有。偵測到系統的「減少動態效果」設定時,錯誤抖動、字元 pop 與成功波紋的縮放都會停用,caret 也不再明滅。