レビュー済み·難易度: 初級·更新: 2026-04-18

React でフォームフィールドにヘルプテキストとエラーメッセージを表示する

入力フィールドの下にヘルプテキスト(説明)とバリデーションエラーメッセージを表示し、スクリーンリーダー対応の aria-describedby で関連付けるアクセシブルなフォームパターン。

nextjsui-componentform

対応バージョン

nextjs 15react 19

前提環境

React の useState と基本的なフォーム実装を理解していること

概要

フォームフィールドにヘルプテキスト(常時表示の説明)とバリデーションエラーメッセージ(エラー時のみ表示)を組み合わせ、aria-describedby で入力欄と関連付けるアクセシブルなフィールドコンポーネントを実装する。エラーとヘルプのどちらが aria-describedby を取るかの制御を含む。

インストール

# 追加インストールは不要

実装

フィールドコンポーネント

// components/FormField.tsx
import { useId } from "react";

type Props = {
  label: string;
  helpText?: string;
  error?: string;
  children: (props: {
    id: string;
    "aria-describedby"?: string;
    "aria-invalid"?: boolean;
  }) => React.ReactNode;
};

export function FormField({ label, helpText, error, children }: Props) {
  const id = useId();
  const helpId = helpText ? `${id}-help` : undefined;
  const errorId = error ? `${id}-error` : undefined;

  // エラーがある場合はエラー ID を優先、なければヘルプ ID
  const describedBy = errorId ?? helpId;

  return (
    <div className="flex flex-col gap-1">
      <label htmlFor={id} className="text-sm font-medium text-gray-700">
        {label}
      </label>

      {children({
        id,
        "aria-describedby": describedBy,
        "aria-invalid": !!error,
      })}

      {helpText && !error && (
        <p id={helpId} className="text-xs text-gray-500">
          {helpText}
        </p>
      )}

      {error && (
        <p id={errorId} role="alert" className="text-xs text-red-600">
          {error}
        </p>
      )}
    </div>
  );
}

使用例: テキスト入力

// components/EmailField.tsx
"use client";

import { useState } from "react";
import { FormField } from "./FormField";

export function EmailField() {
  const [value, setValue] = useState("");
  const [error, setError] = useState("");

  const validate = () => {
    if (!value) {
      setError("メールアドレスを入力してください");
    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
      setError("正しいメールアドレスを入力してください");
    } else {
      setError("");
    }
  };

  return (
    <FormField
      label="メールアドレス"
      helpText="登録済みのメールアドレスを入力してください"
      error={error}
    >
      {({ id, ...ariaProps }) => (
        <input
          id={id}
          type="email"
          value={value}
          onChange={(e) => setValue(e.target.value)}
          onBlur={validate}
          className={`rounded border px-3 py-2 text-sm focus:outline-none focus:ring-1 ${
            error
              ? "border-red-400 focus:ring-red-400"
              : "border-gray-200 focus:ring-blue-400"
          }`}
          {...ariaProps}
        />
      )}
    </FormField>
  );
}

セレクトボックスへの適用

// components/CategorySelect.tsx
import { FormField } from "./FormField";

type Props = {
  value: string;
  onChange: (v: string) => void;
  error?: string;
};

export function CategorySelect({ value, onChange, error }: Props) {
  return (
    <FormField
      label="カテゴリ"
      helpText="最も近いカテゴリを選択してください"
      error={error}
    >
      {({ id, ...ariaProps }) => (
        <select
          id={id}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          className="rounded border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-blue-400"
          {...ariaProps}
        >
          <option value="">選択してください</option>
          <option value="routing">ルーティング</option>
          <option value="form">フォーム</option>
          <option value="testing">テスト</option>
        </select>
      )}
    </FormField>
  );
}

ポイント

  • useId() で一意の ID を生成する。Math.random() や連番より安全で、SSR と CSR で ID が一致することが保証されている
  • aria-describedby にはエラー ID とヘルプ ID のどちらを渡すかを制御する。エラーがあるときはエラーの方を優先し、スクリーンリーダーがエラー内容を読み上げやすくする
  • role="alert" をエラーメッセージに付けると、動的に表示されたとき aria ライブリージョンとしてスクリーンリーダーに通知される
  • aria-invalid={!!error} をフィールドに渡すことで、入力欄が無効状態であることを支援技術に伝えられる
  • render prop パターン(children として関数を渡す)で実装することで、inputselecttextarea のどれにでも同じ FormField を適用できる
  • helpText はエラーがないときのみ表示する。エラーとヘルプを同時に表示するとメッセージが混在してユーザーが混乱する

注意点

react-controlled-uncontrolled は制御・非制御コンポーネントの使い分け。react-clear-search-button は検索フィールドの入力補助。これはヘルプテキスト・エラーメッセージの表示制御と aria-describedby による支援技術対応に特化。

関連サンプル