Version 7.0.0 includes a complete TypeScript rewrite (migrated from Flow). While the runtime behavior remains largely unchanged, there are several TypeScript-specific breaking changes you need to be aware of.
In v7.0.0, most FormState boolean properties can be undefined:
❌ Before (v6.x):
const { dirty, pristine, valid } = formState;
if (dirty && !pristine) { // Works fine
// ...
}✅ After (v7.0.0):
const { dirty, pristine, valid } = formState;
if ((dirty ?? false) && !(pristine ?? true)) { // Must handle undefined
// ...
}Affected properties:
dirty,pristine,valid,invaliddirtySinceLastSubmit,modifiedSinceLastSubmitsubmitFailed,submitSucceeded,submitting,validatinghasSubmitErrors,hasValidationErrors
Note: values is still guaranteed to be defined.
❌ Before (v6.x):
import { FieldMetaState } from 'react-final-form';
const meta: FieldMetaState = { /* ... */ };✅ After (v7.0.0):
import { FieldRenderProps } from 'react-final-form';
const meta: FieldRenderProps<any>['meta'] = { /* ... */ };
// Or define it locally:
type FieldMetaState = {
active?: boolean;
data?: Record<string, any>;
dirty?: boolean;
// ... etc
};❌ Before (v6.x):
import { AnyObject } from 'react-final-form';✅ After (v7.0.0):
// Define locally:
type AnyObject = Record<string, any>;❌ Before (v6.x):
const config: UseFieldConfig<string> = {
validate: (value) => value ? undefined : 'Required'
};✅ After (v7.0.0):
const config: UseFieldConfig = {
validate: (value) => value ? undefined : 'Required'
};In v6.x, you could pass arbitrary props (like style, className) directly to <Form>. In v7.0.0, this is no longer supported due to stricter TypeScript typing.
❌ Before (v6.x):
<Form
onSubmit={handleSubmit}
style={{ padding: '20px' }}
className="my-form"
>
{/* ... */}
</Form>✅ After (v7.0.0):
<Form onSubmit={handleSubmit}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit} style={{ padding: '20px' }} className="my-form">
{/* ... */}
</form>
)}
</Form>
// Or wrap in a div:
<div style={{ padding: '20px' }} className="my-form">
<Form onSubmit={handleSubmit}>
{/* ... */}
</Form>
</div>If you're also upgrading final-form to v5.0.0, be aware of these changes:
❌ Before (v4.x):
const mockFormState: InternalFormState = {
values: {},
// ...
};✅ After (v5.0.0):
const mockFormState: InternalFormState = {
values: {},
asyncErrors: {}, // Now required
// ...
};❌ Before (v4.x):
const mutator: Mutator = (args, state, tools) => {
// ...
};✅ After (v5.0.0):
// If you get type errors with existing mutators:
const mutator = ((args, state, tools) => {
// ...
}) as unknown as Mutator;For a medium to large codebase, expect to modify 100+ files. Here's a recommended approach:
-
Update dependencies:
npm install react-final-form@^7.0.0 final-form@^5.0.0
-
Fix compilation errors in this order:
- Handle optional boolean properties (use
?? falseor?? true) - Replace
FieldMetaStateimports withFieldRenderProps['meta'] - Replace
AnyObjectimports with local type definition - Remove generic from
UseFieldConfig<T>→UseFieldConfig - Fix
<Form>props (move styling to wrapper or inner<form>)
- Handle optional boolean properties (use
-
Test thoroughly:
- All form submissions
- Validation behavior
- Field state management
- Meta information display
-
Update mocks/tests:
- Add
asyncErrors: {}to InternalFormState mocks - Cast mutators if needed
- Add
If you encounter issues during migration:
- Check the TypeScript examples
- Review closed issues
- Open a new issue with a reproduction
Despite the migration effort, v7.0.0 brings significant benefits:
- Better TypeScript support - First-class TypeScript instead of generated types from Flow
- Improved type inference - Better autocomplete and type checking
- Modern codebase - Easier for contributors to work with
- Long-term maintainability - TypeScript ecosystem is more active than Flow
Version: 7.0.0
Last Updated: 2026-02-13