Professionally I've been working on a reporting tool for the past year. The frontend of that tool consists only of a couple of views and one of them is a relatively complex form, which is also the heart of this reporting tool.
It consists of a lot of features that make the implementation of a form quite challenging. There are dropdown fields filled with items dynamically loaded from an API endpoint (1).
Other dropdowns show a list of items based on the selection of a dependent field (2).
Imagine a price value and a commission percentage. Multiplying those values equals the value of the commission input. Should the change of the commission now change the percentage or the price value?
And to make things even more exciting, complete parts of the form are exchanged based on other fields (4).
It makes sense to choose a library to handle all the internals of form state handling, validation and so on not to reinvent the wheel. Formik is such a library: It has earned more than 28.5k stars on GitHub (link) and is even mentioned briefly in the React documentation (link).
Formik comes with a couple of appealing features: Support for a custom validation
mechanism, provides hooks to read from and update the state as well as a couple
of standard handlers for
onBlur. It also has a component to deal
with lists and offers functions to deal with them accordingly (e.g. adding and
With more than twenty fields with such special behaviors as described previously, each of which reacts to every key press, a form library is challenged not only in terms of its functionality, but also in terms of performance.
And indeed an input lag was noticeable which led to the discovery of Formik's
FastField component (link):
<FastField />is an optimized version of
<Field />meant to be used on large forms (~30+ fields) or when a field has very expensive validation requirements.
The problem is that every field update causes the whole form state to be rewritten.
This on the other hand triggers every field that is either rendered with the
<Field /> component or uses the
This is calling for trouble and the sheer co-existence of
<FastField /> raises
the question, why the other components are not optimized for performance from the
One a half years ago a GitHub issue was opened to raise awareness to the performance
issues that come with the
useField() hook, but it's not resolved. According to a
GitHub user commenting in this thread (link)
Formik's architecture makes it nearly impossible to implement an efficient solution.
Instead he proposes to rely on
React.memo to avoid unnecessary rerenders.
Unfortunately that's close to the solution we were forced to apply in order to
prevent those costly rerenders: Intead of wrapping the
useField() hook, which is
smart, we decided to wrap our custom input field components, like the money input
field and the dropdowns for example.
Formik offers a clean documentation, a great set of hooks and components to get the job done, and is a sufficient solution for smaller forms. Due to the serious performance bottlenecks in complex forms and the lack of improvement over the course of the past one and a half years, a choice in favor of Formik should be considered carefully.
React Final Form (link) is another library that offers "high performance subscription-based form state management":
For small forms, redrawing your entire form on every keypress is no problem. But when your form grows, performance can degrade.
No other form library allows such fine tuning to manage exactly which form elements get notified of form state changes.
Its API is quite similar to Formik in a lot of components, and therefore it's relatively easy to switch to this solution. There is even a migration guide available (link).
Additional features, like decorators (link), only add to its value and reduce the amount of code that needs to implemented manually otherwise.