final-form、formik、react-hook-form、redux-form はすべて React アプリケーションでフォームを管理するためのライブラリです。これらのライブラリは、フォームの状態管理、バリデーション、サブミット処理などの共通課題を解決しますが、アーキテクチャやパフォーマンス特性、API設計において明確な違いがあります。redux-form は Redux ストアに依存し、final-form はフレームワーク非依存のコアロジックを提供します。formik は React コンテキストとフックを活用した高レベルな抽象化を提供し、react-hook-form は uncontrolled コンポーネントとネイティブ DOM API を中心とした最適化されたアプローチを採用しています。
React でフォームを扱うのは一見簡単ですが、バリデーション、エラーハンドリング、パフォーマンス、テスト可能性などを考慮すると、専用ライブラリの導入が現実的です。ここでは、代表的な4つのフォームライブラリ — final-form、formik、react-hook-form、redux-form — を、実際の開発現場で直面する課題を中心に比較します。
重要:
redux-formは npm ページ および GitHub リポジトリ で非推奨(deprecated)と明記されています。新規プロジェクトでは使用しないでください。
formik:Controlled コンポーネント中心formik は React の controlled コンポーネントモデルを全面的に採用しています。フォームの各フィールドの値は React の state で管理され、変更のたびに再レンダリングが発生します。
// formik
import { useFormik } from 'formik';
function MyForm() {
const formik = useFormik({
initialValues: { email: '' },
onSubmit: values => alert(JSON.stringify(values, null, 2)),
validate: values => {
const errors = {};
if (!values.email) errors.email = '必須です';
return errors;
}
});
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}
<button type="submit">送信</button>
</form>
);
}
react-hook-form:Uncontrolled コンポーネント + Ref 登録react-hook-form は、uncontrolled コンポーネントを基本とし、DOM の ref を使って値を直接取得します。これにより、ユーザー入力時の再レンダリングを最小限に抑えます。
// react-hook-form
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
return (
<form onSubmit={handleSubmit(data => alert(JSON.stringify(data)))}>
<input
{...register('email', { required: '必須です' })}
/>
{errors.email && <div>{errors.email.message}</div>}
<button type="submit">送信</button>
</form>
);
}
final-form:フレームワーク非依存のエンジンfinal-form 自体は純粋な JavaScript ライブラリで、UI フレームワークに依存しません。React で使うには react-final-form などのバインディングが必要です。内部では、フィールドの状態をサブスクリプションベースで管理します。
// final-form (with react-final-form)
import { Form, Field } from 'react-final-form';
const MyForm = () => (
<Form
onSubmit={values => alert(JSON.stringify(values))}
validate={values => {
const errors = {};
if (!values.email) errors.email = '必須です';
return errors;
}}
render={({ handleSubmit, errors }) => (
<form onSubmit={handleSubmit}>
<Field name="email">
{({ input, meta }) => (
<>
<input {...input} />
{meta.touched && meta.error && <div>{meta.error}</div>}
</>
)}
</Field>
<button type="submit">送信</button>
</form>
)}
/>
);
redux-form:Redux ストアに状態を保存(非推奨)redux-form は、フォームの全状態(値、エラー、タッチ状態など)を Redux ストアに保存します。これは、大規模アプリでは不要な再レンダリングやメモリ消費を引き起こします。
// redux-form (非推奨)
import { reduxForm, Field } from 'redux-form';
const renderField = ({ input, meta: { touched, error } }) => (
<>
<input {...input} />
{touched && error && <div>{error}</div>}
</>
);
let MyForm = props => (
<form onSubmit={props.handleSubmit(values => alert(JSON.stringify(values)))}>
<Field name="email" component={renderField} />
<button type="submit">送信</button>
</form>
);
MyForm = reduxForm({ form: 'myForm' })(MyForm);
formik:デフォルトでは、1つのフィールドの変更がフォーム全体の再レンダリングを引き起こします。useField や memo による最適化は可能ですが、手間がかかります。react-hook-form:フィールドは個別に登録され、変更があってもそのフィールドのみが影響を受けます。デフォルトで最適化されています。final-form:サブスクリプション機能により、特定のフィールドの変更だけを監視できます。適切に設定すれば、再レンダリングを最小限に抑えられます。redux-form:フォームの任意の変更が Redux ストアを更新し、接続されたすべてのコンポーネントが再レンダリングされる可能性があります。パフォーマンス上の問題が頻発します。formik:validate 関数または validationSchema(Yup など)で定義。非同期バリデーションは validate 関数内で Promise を返すことで可能。
// formik 非同期バリデーション
const validate = async (values) => {
const errors = {};
const exists = await checkEmailExists(values.email);
if (exists) errors.email = 'このメールは既に使用されています';
return errors;
};
react-hook-form:register のオプションで同期バリデーションを定義。非同期バリデーションは resolver または validate 関数で実装。
// react-hook-form 非同期バリデーション
<input
{...register('email', {
validate: async (value) => {
const exists = await checkEmailExists(value);
return exists ? 'このメールは既に使用されています' : true;
}
})}
/>
final-form:validate 関数で同期バリデーション。非同期バリデーションは asyncValidate またはカスタムロジックで実装可能。
// final-form 非同期バリデーション(簡略化)
<Form
validate={values => {
// 同期バリデーション
}}
// 非同期はカスタムフックなどで対応
>
redux-form:validate および asyncValidate プロップで対応可能でしたが、非推奨のため詳細は割愛。
formik:v2 以降、TypeScript サポートが大幅に改善。initialValues の型から自動的にフォーム値の型が推論されます。react-hook-form:ゼロランタイムコストで完全な TypeScript サポート。ジェネリクスでフォーム値の型を明示的に指定可能。final-form:コアライブラリは TypeScript 対応。react-final-form も型定義を提供。redux-form:型定義は存在しますが、非推奨のため今後の更新は期待できません。react-hook-form:DOM ベースのアプローチのため、React Testing Library との相性が非常に良い。fireEvent.change で直接入力値を操作可能。formik:controlled コンポーネントのため、テストは直感的ですが、内部状態の操作には act や waitFor が必要になる場合があります。final-form:テストは可能ですが、サブスクリプションモデルのため若干複雑になることがあります。redux-form:Redux ストアのモックが必要で、テストセットアップが煩雑です。formik:React 専用。Yup との統合が一般的。react-hook-form:React 専用。Resolver 経由で Yup、Zod、Joi など多数のバリデーションライブラリと連携可能。final-form:フレームワーク非依存。React、Vue、Angular などさまざまなバインディングがコミュニティ提供。redux-form:Redux に強く依存。現代の React アーキテクチャには不向き。| 観点 | final-form | formik | react-hook-form | redux-form |
|---|---|---|---|---|
| 推奨度 | ◯(特定用途向け) | ◯(小〜中規模フォーム) | ◎(ほとんどのケース) | ✗(非推奨) |
| パフォーマンス | 良(サブスクリプション制御) | 普(最適化要) | 優(デフォルトで最適化) | 悪(ストア依存) |
| 学習コスト | 中(抽象度高) | 低(直感的) | 中(uncontrolled 概念) | 低(だが非推奨) |
| TypeScript | 良 | 良 | 優 | 可(非推奨) |
| 拡張性 | 優(フレームワーク非依存) | 良(Yup 統合) | 優(多様な resolver) | 悪(Redux 依存) |
react-hook-form を検討してください。パフォーマンス、DX、TypeScript サポートのバランスが最も優れています。formik がシンプルで導入しやすいでしょう。final-form のコアエンジンを活用できます。redux-form を使っているプロジェクト:早急に移行を検討しましょう。final-form は redux-form の作者が後継として開発したもので、移行パスも比較的スムーズです。フォームはユーザー体験の要です。適切なライブラリを選ぶことで、開発速度とアプリケーションの品質を両立できます。
formik は、controlled コンポーネントを中心に据え、開発体験を重視するプロジェクトに最適です。TypeScript サポートが充実しており、バリデーションスキーマ(Yup など)との統合も容易です。ただし、大規模なフォームでは再レンダリングのオーバーヘッドが発生しやすいため、パフォーマンス要件が高い場合は注意が必要です。
final-form は、フレームワークに依存しない軽量なフォームエンジンを必要とする場合に適しています。特に、React 以外の UI ライブラリ(例:Vue、Svelte)と統合したい場合や、独自の React バインディングを構築したい場合に有効です。ただし、React 専用の高レベル API は提供しないため、追加のラッパー実装が必要になることがあります。
react-hook-form は、uncontrolled コンポーネントとネイティブ DOM API を活用することで、最小限の再レンダリングと高速なパフォーマンスを実現します。特に複雑で大規模なフォームや、パフォーマンスが重要なアプリケーションに強く推奨されます。また、TypeScript との親和性も高く、バリデーションルールを直感的に記述できます。
redux-form は公式に非推奨(deprecated)とされており、新規プロジェクトでの使用は避けるべきです。Redux ストアにフォーム状態を保存するアーキテクチャは、不要な再レンダリングやメモリ使用量の増加を引き起こすことが多く、現代の React アプリケーションには不向きです。既存の redux-form を使っているプロジェクトは、final-form や react-hook-form への移行を検討すべきです。
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!