OTP Input
驗證碼分格輸入:單一隱形 input 蓋在視覺格子上,支援貼上、錯誤抖動、成功波紋泛綠。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add otp-inputOTP Input 是用 React、Tailwind CSS 與 Motion 做的一次性密碼(OTP)/簡訊驗證碼輸入框:畫面上是分開的格子,底下其實只有一個原生 input,所以貼上、自動填入與刪除都照瀏覽器原本的行為運作。
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
<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 | 型別 | 預設值 | 說明 |
|---|---|---|---|
length | number | 6 | 格數 |
value | string | — | 受控值;不傳則由元件內部管理 |
onChange | (value: string) => void | — | 值變更時觸發 |
onComplete | (code: string) => void | — | 輸入滿 length 位時觸發,回傳完整驗證碼 |
error | boolean | false | 錯誤狀態:整排 x 軸抖動、邊框轉紅 |
success | boolean | false | 成功狀態:格子依序波紋泛綠 |
type | 'numeric' | 'alphanumeric' | 'numeric' | 允許的字元類型;alphanumeric 會自動轉大寫 |
disabled | boolean | false | 停用輸入 |
可及性
採「單一隱形 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 也不再明滅。