輸入元件

表單欄位

表單欄位容器 React 元件:標籤、說明、錯誤訊息與前後綴整合,錯誤以高度展開收合不跳版,前後綴與輸入框共用聚焦外框,可用 render prop 包任何控制項。

把一個表單欄位該有的東西收進同一個容器:標籤、標籤下的說明、欄位下的提示、錯誤訊息、必填星號、前後綴與字數。錯誤出現時以高度從 0 展開、消失時反向收合,下方欄位會平順地讓位而不是突然跳動;錯誤文字改變時只交叉淡接,不重做高度動畫。前綴、輸入框、後綴包在同一個外框裡,聚焦時一起亮起 ring,並有一條 2px 底線從中心向兩側展開。不給 children 時渲染內建 input(Enter 有 IME 組字保護、字數以字元計算);給 render prop 就能包你自己的 select、textarea 或任何控制項,id、aria-describedby、aria-invalid 都會自動接好。

把這個效果裝進你的專案

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

npx webberui@latest add form-field

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

Playground

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

180
編輯中的呼叫預設屬性
<FormField/>

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

安裝

npx webberui@latest add https://webberui.com/r/form-field.json

或在 components.json 設定 registries 後,改用 @webberui/form-field 安裝。

使用

import { Mail } from "lucide-react";
import { FormField } from "@/components/ui/form-field";

// 內建 input
<FormField
  label="電子郵件"
  required
  prefix={<Mail aria-hidden />}
  hint="我們不會公開你的電子郵件"
  error={emailError}
  inputProps={{
    type: "email",
    value: email,
    onChange: (value) => setEmail(value),
    onEnter: () => submit(),
  }}
/>

// render prop:包你自己的控制項
<FormField label="所在縣市" required error={cityError}>
  {(field) => (
    <select {...field} value={city} onChange={(e) => setCity(e.target.value)}>
      <option value="">請選擇</option>
      <option value="臺北市">臺北市</option>
    </select>
  )}
</FormField>

Props

Prop型別預設值說明
labelReactNode—欄位標籤
descriptionReactNode—標籤下方的說明文字
hintReactNode—欄位下方的灰色提示;error 出現時被取代
errorReactNode—錯誤訊息;有值即進入錯誤狀態
requiredbooleanfalse標籤後加紅色「*」,並對內建 input 加 required 與 aria-required
orientation"vertical" | "horizontal""vertical"排列方向;horizontal 時標籤欄固定寬並靠右,手機寬度自動回落為 vertical
prefixReactNode—輸入框前綴:文字(灰底分段)、icon 或按鈕(透明)
suffixReactNode—輸入框後綴:文字(灰底分段)、icon 或按鈕(透明)
maxLengthnumber—字數上限(以 Array.from 計算字元,中文與 emoji 各算一字);僅內建 input 生效
showCountbooleanfalse在右下角顯示字數「12 / 50」;僅內建 input 生效
durationnumber180錯誤訊息展開收合的動畫時間(毫秒)
disabledbooleanfalse停用欄位
inputPropsFormFieldInputProps—內建 input 的屬性
children(field: FormFieldControlProps) => ReactNode—render prop;不給時渲染內建 input
classNamestring—透傳到最外層容器

FormFieldInputProps

其餘原生 <input> 屬性(type、placeholder、name、onBlur、autoComplete 等)都會透傳。

欄位型別預設值說明
valuestring—受控輸入值;未提供時由元件內部管理(非受控)
defaultValuestring""非受控模式的初始值
onChange(value: string, event) => void—輸入值變動時回呼(已依 maxLength 以字元數截斷)
onEnter(value: string, event) => void—按下 Enter 時回呼;IME 組字中按 Enter 不會觸發

FormFieldControlProps

欄位型別預設值說明
idstring—由 useId 產生,標籤以 htmlFor 指向它
aria-describedbystring | undefined—串起說明、提示/錯誤、字數的 id
aria-invalidboolean—是否處於錯誤狀態
aria-requiredtrue | undefined—required 時為 true
disabledboolean | undefined—是否停用
classNamestring—去除邊框與背景的控制項樣式,讓它與前後綴共用外框

細節

  • 不跳版的錯誤動畫:錯誤區以 height: 0 → auto 加淡入展開,時間由 duration 控制(預設 180ms、easeOut);提示與錯誤互相取代時兩者同時收合/展開,總高度連續變化。錯誤文字變更時新舊兩則疊在同一格交叉淡接,不重做高度動畫。
  • 共用外框:前綴、控制項、後綴放在同一個 flex 容器,以 :focus-within 顯示 ring 與較深的邊框;錯誤時改為紅框。純文字前後綴(如 "https://"、".example.tw")畫成灰底分段,icon 與按鈕則透明貼齊。
  • 聚焦回饋:標籤文字由 neutral-600 變為 neutral-950(深色模式反向),外框下緣一條 2px 線從中心向兩側展開;兩者時間由 --wb-form-field-focus-duration(預設 150ms)控制。外層另以 style 寫入 --wb-form-field-duration,值等於 duration。
  • 字數以 Array.from(value).length 計算,中文與 emoji 各算一字;IME 組字中不截斷,組字結束才依 maxLength 截斷,避免打斷注音/拼音輸入。
  • 內建 input 的 onEnter 有 IME 組字保護:中文等輸入法組字中按 Enter 不會觸發,並處理 Safari 在 compositionend 後才發 keydown 的情況。
  • render prop 收到的 className 是無邊框、透明底的控制項樣式;直接展開 {...field} 即可與外框融為一體,要追加樣式就自己串接。

可及性

  • 標籤以 htmlFor 關聯到控制項;aria-describedby 依序串起說明、提示或錯誤、字數
  • 錯誤時控制項設定 aria-invalid,錯誤區為 role="alert",出現與改字都會被螢幕閱讀器朗讀;錯誤以 CircleAlert 圖示加文字呈現,不只靠顏色
  • required 時內建 input 加 required 與 aria-required,render prop 也會收到 aria-required;紅色星號對輔助科技隱藏
  • 聚焦外框以 :focus-within 呈現,鍵盤與滑鼠聚焦都看得到
  • 使用者系統開啟「減少動態效果」時,錯誤直接出現/消失(只淡入 100ms),底線不展開只變色