formik、formik-material-ui、react-final-form、react-hook-form、react-jsonschema-form、redux-form はすべて React アプリケーションにおけるフォーム状態管理、バリデーション、送信処理を支援するライブラリです。これらのライブラリは、フォームの複雑さや開発者の好みに応じて異なるアプローチを取ります。formik と react-final-form はコンポーネントベースの制御を重視し、react-hook-form は uncontrolled コンポーネントと React Hooks を活用してパフォーマンスを最適化します。formik-material-ui は formik の上に Material UI との統合レイヤーを提供します。react-jsonschema-form は JSON Schema からフォームを自動生成するユースケースに特化しています。一方、redux-form は Redux ストアにフォーム状態を保存する古くからのアプローチを採用していますが、現在は非推奨となっています。
Reactアプリケーションでフォームを扱う際、状態管理、バリデーション、送信処理、エラーハンドリングなど多くの面倒事を自分で実装するのは非効率です。そのため、専用のフォーム管理ライブラリが広く使われています。ここでは、代表的な6つのパッケージ — formik、formik-material-ui、react-final-form、react-hook-form、react-jsonschema-form、redux-form — を技術的観点から比較します。
重要:
redux-formは npm ページおよび GitHub リポジトリで公式に非推奨(deprecated)とされています。新規プロジェクトでは使用しないでください。
まずは、メールアドレスとパスワードを入力するシンプルなログインフォームを、各ライブラリでどう実装するかを見てみましょう。
formikformik は controlled コンポーネント方式で、内部状態を管理します。useFormik フックまたは <Formik> コンポーネントでフォームをラップします。
import { useFormik } from 'formik';
import * as yup from 'yup';
const validationSchema = yup.object({
email: yup.string().email().required(),
password: yup.string().min(8).required()
});
function LoginForm() {
const formik = useFormik({
initialValues: { email: '', password: '' },
validationSchema,
onSubmit: (values) => console.log(values)
});
return (
<form onSubmit={formik.handleSubmit}>
<input
name="email"
value={formik.values.email}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
{formik.touched.email && formik.errors.email ? <div>{formik.errors.email}</div> : null}
<input
name="password"
type="password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
{formik.touched.password && formik.errors.password ? <div>{formik.errors.password}</div> : null}
<button type="submit">ログイン</button>
</form>
);
}
formik-material-uiこれは formik と Material UI の橋渡しをするだけのライブラリです。formik のロジックはそのまま、UI コンポーネントだけを置き換えます。
import { useFormik } from 'formik';
import { TextField, Button } from '@mui/material';
import { TextField as FormikTextField } from 'formik-material-ui';
function LoginForm() {
const formik = useFormik({
initialValues: { email: '', password: '' },
onSubmit: (values) => console.log(values)
});
return (
<form onSubmit={formik.handleSubmit}>
<FormikTextField name="email" label="メールアドレス" fullWidth />
<FormikTextField name="password" label="パスワード" type="password" fullWidth />
<Button type="submit">ログイン</Button>
</form>
);
}
react-final-formreact-final-form は render props パターンを使い、フィールドごとの再レンダリングを最適化します。
import { Form, Field } from 'react-final-form';
import { required, email, minLength } from 'react-final-form-validators';
const validate = (values) => {
const errors = {};
if (!values.email) errors.email = '必須です';
else if (!/^[^@]+@[^@]+$/.test(values.email)) errors.email = 'メール形式で入力してください';
if (!values.password) errors.password = '必須です';
else if (values.password.length < 8) errors.password = '8文字以上にしてください';
return errors;
};
function LoginForm() {
return (
<Form
onSubmit={(values) => console.log(values)}
validate={validate}
render={({ handleSubmit, pristine, invalid }) => (
<form onSubmit={handleSubmit}>
<Field name="email">
{({ input, meta }) => (
<div>
<input {...input} placeholder="メールアドレス" />
{meta.error && meta.touched && <span>{meta.error}</span>}
</div>
)}
</Field>
<Field name="password">
{({ input, meta }) => (
<div>
<input {...input} type="password" placeholder="パスワード" />
{meta.error && meta.touched && <span>{meta.error}</span>}
</div>
)}
</Field>
<button type="submit" disabled={pristine || invalid}>ログイン</button>
</form>
)}
/>
);
}
react-hook-formreact-hook-form は uncontrolled コンポーネントを基本とし、register でフィールドを登録します。バリデーションはスキーマまたはビルトインルールで定義可能です。
import { useForm } from 'react-hook-form';
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
defaultValues: { email: '', password: '' }
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register('email', {
required: '必須です',
pattern: { value: /^[^@]+@[^@]+$/, message: 'メール形式で入力してください' }
})}
placeholder="メールアドレス"
/>
{errors.email && <span>{errors.email.message}</span>}
<input
{...register('password', {
required: '必須です',
minLength: { value: 8, message: '8文字以上にしてください' }
})}
type="password"
placeholder="パスワード"
/>
{errors.password && <span>{errors.password.message}</span>}
<button type="submit">ログイン</button>
</form>
);
}
react-jsonschema-formこのライブラリは JSON Schema からフォームを自動生成します。UI スキーマで見た目を調整可能です。
import Form from 'react-jsonschema-form';
const schema = {
type: 'object',
properties: {
email: { type: 'string', format: 'email', title: 'メールアドレス' },
password: { type: 'string', minLength: 8, title: 'パスワード' }
},
required: ['email', 'password']
};
const uiSchema = {
password: { 'ui:widget': 'password' }
};
function LoginForm() {
const onSubmit = ({ formData }) => console.log(formData);
return (
<Form
schema={schema}
uiSchema={uiSchema}
onSubmit={onSubmit}
/>
);
}
redux-form(非推奨)redux-form は Redux ストアにフォーム状態を保存します。新規プロジェクトでは使用しないでください。
// 非推奨のため、参考までに記載
import { reduxForm, Field } from 'redux-form';
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<input {...input} placeholder={label} type={type} />
{touched && error && <span>{error}</span>}
</div>
);
let LoginForm = (props) => {
const { handleSubmit } = props;
return (
<form onSubmit={handleSubmit((values) => console.log(values))}>
<Field name="email" component={renderField} label="メールアドレス" />
<Field name="password" component={renderField} label="パスワード" type="password" />
<button type="submit">ログイン</button>
</form>
);
};
LoginForm = reduxForm({ form: 'login' })(LoginForm);
formikformik はフォーム全体の状態を1つのオブジェクトで管理するため、1つのフィールドを更新してもフォーム全体が再レンダリングされる可能性があります。useMemo や React.memo で子コンポーネントを最適化する必要があります。
react-final-formフィールド単位で状態を分離し、変更があったフィールドだけを再レンダリングする仕組みを備えています。これにより、大規模フォームでも高いパフォーマンスを維持できます。
react-hook-formuncontrolled コンポーネントを採用しているため、React の状態管理を介さず、DOM から直接値を取得します。これにより、不要な再レンダリングが発生せず、パフォーマンスが非常に優れています。
react-jsonschema-formフォーム全体を1つのコンポーネントとして扱うため、フィールドの変更時に全体が再レンダリングされます。複雑なフォームではパフォーマンスのボトルネックになる可能性があります。
redux-formRedux ストアに状態を保存するため、フィールド変更のたびにアクションがディスパッチされ、ストアが更新されます。大規模アプリではパフォーマンスに悪影響を及ぼすため、非推奨となっています。
formik: Yup や他のバリデーションライブラリと連携可能。非同期バリデーションもサポート。react-final-form: 同期・非同期バリデーションをカスタム関数で自由に実装可能。react-hook-form: ビルトインルール、Yup、Zod、Joi など多彩なバリデーションスキーマに対応。非同期バリデーションも可能。react-jsonschema-form: JSON Schema の標準バリデーションルールに依存。カスタムバリデーションは可能だがやや制限あり。redux-form: カスタムバリデーション関数をサポートしていたが、非推奨のため新規開発では考慮不要。formik: useField や FieldArray を使って動的リストやネストされたオブジェクトを簡単に扱えます。react-final-form: FieldArray 相当の機能はコミュニティプラグインが必要。ネイティブサポートは弱め。react-hook-form: useFieldArray フックで動的フィールドを強力にサポート。ネストも問題なし。react-jsonschema-form: JSON Schema の array や object 型で表現可能ですが、UI カスタマイズが難しい場合があります。formik-material-ui は formik 専用の Material UI 統合レイヤーです。他の UI ライブラリ(例: Ant Design、Chakra UI)を使う場合は、それぞれのコミュニティ製統合パッケージを探す必要があります。react-hook-form は uncontrolled 方式のため、どの UI ライブラリとも簡単に統合できます。react-final-form も render props の柔軟性により、任意の UI コンポーネントと接続可能です。| ライブラリ | 推奨されるユースケース | 注意点 |
|---|---|---|
formik | 中規模フォーム、Yup との連携、動的フィールドが必要 | 大規模フォームでは再レンダリングに注意 |
formik-material-ui | formik + Material UI を使うプロジェクト | Material UI 以外では不要 |
react-final-form | 高パフォーマンスが求められる大規模フォーム | render props の記述が冗長になりがち |
react-hook-form | パフォーマンス重視、シンプルなAPI、TypeScript対応 | uncontrolled 方式に慣れる必要あり |
react-jsonschema-form | JSON Schema からフォームを自動生成する必要がある場合 | 複雑なカスタムロジックには不向き |
redux-form | 非推奨 — 新規プロジェクトでは使用禁止 | レガシー保守のみ |
react-hook-form を検討してください。パフォーマンス、シンプルさ、TypeScript サポートのバランスが非常に優れています。formik に慣れているチームなら、formik-material-ui で開発速度を上げられます。react-jsonschema-form が唯一の選択肢です。redux-form は絶対に使わないでください。公式に非推奨であり、現代の React 最適化手法と相性が悪いです。フォームはユーザー体験の要です。適切なライブラリを選ぶことで、開発効率とアプリケーションの品質を同時に高めることができます。
formik は中規模以上のフォームで、バリデーションやフィールド間の依存関係が複雑な場合に適しています。Context API とカスタムフックを組み合わせた柔軟な設計により、ネストされたフィールドや動的フィールドの追加・削除も容易に実装できます。ただし、大規模なフォームでは再レンダリングのオーバーヘッドが発生する可能性があるため、パフォーマンス要件が高い場合は注意が必要です。
formik-material-ui は既に formik を採用しており、UI フレームワークとして Material UI を使用しているプロジェクトに最適です。このライブラリは formik の Field コンポーネントと Material UI の入力コンポーネント(例: TextField)を簡単に接続するためのヘルパーを提供します。ただし、Material UI 以外の UI ライブラリを使用している場合は不要です。
react-final-form は高性能かつ軽量なフォーム管理を必要とするプロジェクトに適しています。render props パターンを採用しており、再レンダリングを最小限に抑える仕組み(フィールド単位の更新通知)を備えています。ただし、render props の記述スタイルは JSX の可読性を下げる可能性があり、Hooks ベースのコードを好むチームには向かないかもしれません。
react-hook-form はパフォーマンスとシンプルさを重視するプロジェクトに最適です。uncontrolled コンポーネントを基本とし、登録されたフィールドのみを監視することで、不要な再レンダリングを回避します。TypeScript との相性も非常に良く、バリデーションルールも直感的に記述できます。ただし、完全に uncontrolled な設計のため、動的フィールドの制御や高度なカスタムロジックには若干の学習コストがあります。
react-jsonschema-form は JSON Schema に基づいてフォームを動的に生成する必要がある場合(例: 設定画面、CMS、API ドキュメントからのフォーム生成)に最適です。UI スキーマを使って見た目をカスタマイズ可能ですが、複雑なインタラクションやカスタムバリデーションロジックが必要な場合は柔軟性に欠けることがあります。一般的な静的フォームにはオーバーエンジニアリングになる可能性があります。
redux-form は公式に非推奨(deprecated)とされており、新規プロジェクトでの使用は避けるべきです。過去に Redux を深く活用していたレガシープロジェクトの保守にはまだ使われることがありますが、新しい開発では formik や react-hook-form などの現代的な代替手段を検討してください。Redux ストアにフォーム状態を保存するアプローチは、不要な状態の肥大化とパフォーマンス劣化を引き起こすことが多いため、現在のベストプラクティスとは見なされていません。
Visit https://formik.org to get started with Formik.
List of organizations and projects using Formik
This monorepo uses yarn, so to start you'll need the package manager installed.
To run E2E tests you'll also need Playwright set up, which can be done locally via npx playwright install. Afterward, run yarn start:app and in a separate tab run yarn e2e:ui to boot up the test runner.
When you're done with your changes, we use changesets to manage release notes. Run yarn changeset to autogenerate notes to be appended to your pull request.
Thank you!
Formik is made with <3 thanks to these wonderful people (emoji key):
Jared Palmer 💬 💻 🎨 📖 💡 🤔 👀 ⚠️ | Ian White 💬 🐛 💻 📖 🤔 👀 | Andrej Badin 💬 🐛 📖 | Adam Howard 💬 🐛 🤔 👀 | Vlad Shcherbin 💬 🐛 🤔 | Brikou CARRE 🐛 📖 | Sam Kvale 🐛 💻 ⚠️ |
|---|---|---|---|---|---|---|
Jon Tansey 🐛 💻 | Tyler Martinez 🐛 📖 | Tobias Lohse 🐛 💻 |
This project follows the all-contributors specification. Contributions of any kind welcome!