sashigane
コンポーネント

Field

ラベル・説明・誤りを、入力ひとつにまとめる

ラベルと入力の関連付けは Field が作ります

ラベルと入力、説明と入力、誤りと入力。どれも忘れると読み上げだけが黙ります。 見た目は正常なので、書き忘れても気づけません。

Field に id を1つ渡せば、ラベル・説明・誤りの関連付けは全部こちらで作ります。

<Field id="mail" label="メールアドレス" description="仕事用のもの" error={errors.mail} required>
  <Input type="email" />
</Field>

利用側が渡すのは id を1つだけです。説明と誤りの id はそこから導きます。

id は必須です

自動で作る道は取っていません。安定した名前を作るには useId が要り、 このコンポーネントがクライアント側になるためです。

必須にしたことで、書き忘れが型で止まります。

バリデーションは持ちません

規則を走らせる仕組みはありません。 誤りの文言を受け取るだけです。

受け取るのは標準の props だけなので、どのライブラリとも組めます—— react-hook-form でも TanStack Form でも Conform でも、素の FormData でも同じです。

{/* react-hook-form の例 */}
<Field id="mail" label="メール" error={formState.errors.mail?.message}>
  <Input {...register('mail')} />
</Field>

実際に動かせます

下は react-hook-form で検証が走る本物のフォームです。 打っている最中に検証が走ります——形が整うと線が緑に変わり、印が出ます。

仕事用のものを入れてください

空でも構いません

Field は react-hook-form に依存していません。 このデモが使っているだけで、 受け取るのは標準の props だけです。register() が返すものをそのまま渡せます。

入力側で同じ属性を書くとエラーになります

id / aria-describedby / aria-invalid は Field が渡します。 入力側で書くとField が渡した値が上書きされ、関連付けだけが切れます—— 見た目は正常なままなので気づけません。だからエラーにしています。

register() が返す name / onChange / onBlur / ref は重なりません。

入力が正しいことも示せます

<Field id="mail" label="メール" valid>
  <Input />
</Field>

境界が変わり、入力の右端に印が出ます。印は読み上げから隠れています—— 出しても「チェック」としか言わないので、状態は境界が伝えます。

誤りと同時には使えません。両方は成り立たないので、誤りが勝ちます。

空の段落は置きません

説明も誤りも無いときは、何も描きません。 置くと、 何も書かれていない行が読み上げに出ます。

必須であることを記号だけで伝えません

* は読み上げに届かないので、文字も一緒に置いています。

3つの状態

通常・空・エッジケースの3つは、どのコンポーネントにも必ずあります。

通常default.tsx

通常。結びつけを利用側に書かせない。

他の人に見える名前です

空empty.tsx

空。説明も誤りも無いとき。

エッジケースedge.tsx

エッジケース。誤りと説明が両方あるとき、必須のとき。

仕事用のものを入れてください

この形では受け取れません

印と境界で表します

何行でも構いません

props

Prop

Type

型と説明はコンポーネントのソースから抽出しています。既定値は出していません——cva の defaultVariants は 署名に現れないので、表に出すと「引数のデフォルトが書かれているものだけ」になり、嘘になります。

On this page