バリデーション・エラー表示

バリデーション・エラー表示は、フォーム入力に対するバリデーション結果をUIに反映する仕組み。必須チェック・文字数制限・メールアドレス形式など、ユーザーの入力内容に問題がある場合にエラーメッセージやスタイルで伝える。

インラインエラーメッセージをフィールド直下に表示するのが最も一般的なパターン。React Hook Form や Zod と組み合わせることで、スキーマ定義とUIを分離したバリデーションが実現できる。

主なバリエーション
  • インラインエラーメッセージ(フィールド直下)
  • エラー時のボーダー色・背景色の変更
  • フォームレベルのバリデーションサマリー
  • リアルタイムバリデーション(入力中)
  • React Hook Form との連携
  • Zod スキーマ連携

ライブラリ横断比較

機能MantineAnt Designshadcn/uiMUI
インラインエラーメッセージ
error prop
help + status
FormMessage
helperText
エラー時のスタイル変更
自動適用
validateStatus
自動適用
error prop
フォームレベルバリデーション
useForm
Form.useForm
handleSubmit
外部ライブラリ推奨
リアルタイムバリデーション
validateInputOnChange
validateTrigger
mode: onChange
カスタム実装
React Hook Form連携
or @mantine/form
Controller利用
ネイティブ対応
Controller利用
Zodスキーマ連携
zodResolver
外部ライブラリ
zodResolver
zodResolver

○ = 対応  △ = 部分対応・制限あり  × = 非対応

ライブラリ別コード例

各ライブラリでバリデーション・エラー表示を実装する際の設定部分を抜粋しています。 動くデモは各比較ページでご確認ください。

Mantine

// @mantine/form でバリデーション
import { useForm } from '@mantine/form';
import { TextInput, PasswordInput, Button, Stack } from '@mantine/core';

function MyForm() {
  const form = useForm({
    initialValues: { email: '', password: '' },
    validate: {
      email: (value) =>
        /^S+@S+$/.test(value) ? null : '有効なメールアドレスを入力してください',
      password: (value) =>
        value.length < 8 ? 'パスワードは8文字以上入力してください' : null,
    },
    validateInputOnChange: true,  // リアルタイムバリデーション
  });

  return (
    <form onSubmit={form.onSubmit(console.log)}>
      <Stack>
        <TextInput
          label="メールアドレス"
          {...form.getInputProps('email')}
          // error prop が自動設定される
        />
        <PasswordInput
          label="パスワード"
          {...form.getInputProps('password')}
        />
        <Button type="submit" disabled={!form.isValid()}>送信</Button>
      </Stack>
    </form>
  );
}

// Zod スキーマ連携(mantine-form-zod-resolver)
import { zodResolver } from 'mantine-form-zod-resolver';
import { z } from 'zod';

const schema = z.object({
  email: z.string().email('有効なメールアドレスを入力してください'),
  age: z.number().min(18, '18歳以上のみ登録可能です'),
});
const form = useForm({ validate: zodResolver(schema), initialValues: { email: '', age: 0 } });

Ant Design

// Form + Form.Item でバリデーション
import { Form, Input, Button } from 'antd';

function MyForm() {
  const [form] = Form.useForm();

  return (
    <Form form={form} onFinish={console.log} layout="vertical">
      <Form.Item
        name="email"
        label="メールアドレス"
        rules={[
          { required: true, message: 'メールアドレスは必須です' },
          { type: 'email', message: '有効なメールアドレスを入力してください' },
          { max: 100, message: '100文字以内で入力してください' },
        ]}
        validateTrigger="onChange"  // リアルタイムバリデーション
      >
        <Input />
      </Form.Item>

      <Form.Item
        name="password"
        label="パスワード"
        rules={[{ min: 8, message: 'パスワードは8文字以上です' }]}
      >
        <Input.Password />
      </Form.Item>

      <Button htmlType="submit" type="primary">送信</Button>
    </Form>
  );
}

shadcn/ui

// react-hook-form + zod + shadcn/ui(推奨パターン)
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import {
  Form, FormControl, FormField, FormItem, FormLabel, FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';

const schema = z.object({
  email: z.string().email('有効なメールアドレスを入力してください'),
  password: z.string().min(8, 'パスワードは8文字以上入力してください'),
});

function MyForm() {
  const form = useForm({
    resolver: zodResolver(schema),
    defaultValues: { email: '', password: '' },
    mode: 'onChange',  // リアルタイムバリデーション
  });

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>メールアドレス</FormLabel>
              <FormControl><Input {...field} /></FormControl>
              <FormMessage /> {/* エラーメッセージを自動表示 */}
            </FormItem>
          )}
        />
        <Button type="submit">送信</Button>
      </form>
    </Form>
  );
}

MUI

// react-hook-form + zod + MUI TextField
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { TextField, Button, Stack } from '@mui/material';

const schema = z.object({
  email: z.string().email('有効なメールアドレスを入力してください'),
  password: z.string().min(8, 'パスワードは8文字以上入力してください'),
});

function MyForm() {
  const { control, handleSubmit } = useForm({
    resolver: zodResolver(schema),
    mode: 'onChange',  // リアルタイムバリデーション
  });

  return (
    <form onSubmit={handleSubmit(console.log)}>
      <Stack spacing={2}>
        <Controller
          name="email"
          control={control}
          render={({ field, fieldState }) => (
            <TextField
              {...field}
              label="メールアドレス"
              error={!!fieldState.error}
              helperText={fieldState.error?.message}  // エラーメッセージ
              fullWidth
            />
          )}
        />
        <Controller
          name="password"
          control={control}
          render={({ field, fieldState }) => (
            <TextField
              {...field}
              label="パスワード"
              type="password"
              error={!!fieldState.error}
              helperText={fieldState.error?.message}
              fullWidth
            />
          )}
        />
        <Button type="submit" variant="contained">送信</Button>
      </Stack>
    </form>
  );
}

まとめ・選び方のヒント

  • 独自バリデーションロジックをシンプルに書きたい → Mantine(useFormvalidate オブジェクトに関数を渡すだけ)・Ant Design(rules 配列で宣言的に記述)
  • React Hook Form + Zod の組み合わせで使いたい → shadcn/ui(公式ドキュメントで推奨・FormMessage で自動表示)・MUI(Controller で既存コンポーネントと統合)
  • リアルタイムバリデーションを簡単に設定したい → Mantine(validateInputOnChange: true 一行)・Ant Design(validateTrigger="onChange")・shadcn/ui(mode: 'onChange'
  • エラースタイルを細かくカスタマイズしたい → Ant Design(validateStatus で warning/error/success を切り替え)・MUI(error + helperText prop)
  • Zod スキーマと密に連携したい → shadcn/ui・MUI(@hookform/resolvers/zod で公式サポート)・Mantine(mantine-form-zod-resolver パッケージ)