React-admin V4: Switching Form Library
React-admin v3 used react-final-form
for form handling ; react-admin v4 uses react-hook-form
instead. Read on to learn why we made the switch and what it changes for you.
Forms Are Hard
Form handling is a complex beast. Default values, sync, and async validation, validation on submit or on blur, global and per-field errors, field arrays, dependent fields, parsing and formatting, auto-save, handling of boolean and number inputs, integration with UI libraries, performance... React comes a bit short when it comes to form handling.
React-admin doesn't manage this complexity by itself. Instead, we rely on a Form library.
It used to be redux-form
in react-admin v2, but its author abandoned it and invited users to use react-final-form
instead. That's what we did in react-admin v3. But now react-final-form
is not getting much attention from its maintainers, and the bug count keeps growing (360 open bugs at the time of writing). Some of these bugs affect react-admin users, with no solution in sight.
We know that managing an open-source library is tedious and not always gratifying, so we're not going to complain about react-final-form
being abandoned. In fact, looking at alternative solutions, we saw that the leader of React form management libraries, formik
, is also kind of abandoned (version 2 is no longer maintained, and version 3 has been in alpha since October 2020).
Maybe because form management is hard.
So we were super pleased to discover react-hook-form
, a library that has a complete feature set, and a robust codebase. At the time of writing, react-hook-form has only one open issue, which is remarkable. And its maintainers are actively working on it, releasing one new version every week.
So we made the decision to use react-hook-form
in react-admin v4. And to sponsor its lead developer, bluebill1049.
The React-Admin Form API Is (Mostly) Unchanged
We replaced the library used by react-admin forms without changing the react-admin form API - for the most part. The same code works in react-admin v3 and v4:
const PostCreate = () => (
<Create>
<SimpleForm>
<TextInput source="title" validate={required()} />
<TextInput source="teaser" fullWidth multiline />
<RichTextInput source="body" fullWidth validate={required()} />
<ReferenceInput source="author" reference="users">
<AutocompleteInput optionText="name" />
</ReferenceInput>
</SimpleForm>
</Create>
);
Maintaining backward compatibility proved challenging, especially for advanced features. We decided to embrace the react-hook-form
API so that users don't have to learn two ways of working with forms (react-admin's and react-hook-form's). This means there are some breaking changes, documented in the react-admin v4 Upgrade Guide. The most striking is that initialValues
is now called defaultValues
, but the upgrade should be as simple as a search and replace.
And beyond the internal handling of forms, react-hook-form
opens many new possibilities.
Validation By Schema
react-hook-form
allows to use external validation libraries (such as Yup, Zod, Joi, Superstruct, Vest and many others) for form validation. React-admin supports this feature through the <Form resolver>
prop.
For instance, to validate a Customer form with Yup:
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import { SimpleForm, TextInput, NumberInput } from 'react-admin';
const schema = yup
.object()
.shape({
name: yup.string().required(),
age: yup.number().required(),
})
.required();
const CustomerCreate = () => (
<Create>
<SimpleForm resolver={yupResolver(schema)}>
<TextInput source="name" />
<NumberInput source="age" />
</SimpleForm>
</Create>
);
Uncontrolled Inputs For Better Performance
By default, react-admin inputs are controlled, meaning their value
changes as the user edits the input value. Something like:
import { useState } from 'react';
const NameForm = ({ onSubmit }) => {
const [name, setName] = useState('');
const handleChange = event => setName(event.target.value);
const handleSubmit = event => {
onSubmit({ name });
event.preventDefault();
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={name} onChange={handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
But controlled components have a cost: in a naive implementation, the entire form renders at every keystroke. This becomes a problem when the form contains many controls. react-hook-form
solves this by supporting uncontrolled components, where form data is handled by the DOM itself. Something like:
import { useRef } from 'react';
const NameForm = ({ onSubmit }) => {
const input = useRef();
const handleSubmit = event => {
onSubmit({ name: input.current.value });
event.preventDefault();
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" ref={input} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
You can leverage the register
function from the FormContext
to register uncontrolled components and improve form performance:
import { useFormContext } from 'react-hook-form';
const NameInput = () => {
const {
register,
formState: { errors },
} = useFormContext();
return (
<input
id="name"
aria-invalid={errors.firstName ? 'true' : 'false'}
{...register('name', { required: true })}
/>
);
};
Built-In DevTools
If you need to inspect the form state, no need for console.log()
! react-hook-form
comes with developer tools:
Simply add the DevTool
component to your form:
import { DevTool } from '@hookform/devtools';
const PostCreate = () => (
<Create>
<SimpleForm>
<TextInput source="title" validate={required()} />
<TextInput source="teaser" fullWidth multiline />
{/* Add the devtool inside the form */}
<DevTool />
</SimpleForm>
</Create>
);
Conclusion
This article only scratches the surface of all the features supported by react-hook-form
, so I can only recommend you to dive into their great documentation.
By switching to react-hook-form
in react-admin v4, we made react-admin forms more robust, more flexible, and faster. Especially for complex forms with dependent inputs and value formatting, our experience is that react-admin feels more intuitive and that we never get blocked. Overall, react-hook-form
increases the react-admin developer experience a lot. I'm sure you'll love it!