Formik in production: 2 years in review
01/21/2024Formik is a popular open source library for React and React Native that helps you manage form state. It does things like tracking values, errors, and visited fields, and takes care of form submission as well. It looks like this:
You can submit HTML forms without a library like Formik, but it can be harder to manage this in a React application. In particular, if you wanted to submit the form without navigating away from the page you’d have to write a bunch of code to stop that from happening and handle this situation in a custom way. Formik is valuable because it does this for you and makes working with this form state easy.
Over the years, Formik has been a very popular choice for me and for many other companies when building React products. But I’ve started to question this. I’ve started to wonder if it actually just… sucks.
Mainly, I’ve been wondering if Formik has proven to be a worthwhile tool for many of the large companies that they promote on their landing page. It would be difficult for me to answer this without consulting each company individually. However, my own experience using Formik at Laurel may provide some insight.
At Laurel, we recently underwent a massive direction shift as many early-stage startups do. For about 6 years we were building a desktop windows product, which had been received fairly well. However, distribution of that product had become difficult - it was not going to scale well. Anthony, a fellow engineer, and I had noticed this problem and thought, “I bet a website would make it easier to control distribution.” So, we built one.
It was an MVP. We tried to replicate only the core features of the existing product and connect this new web interface to our existing backend services without requiring any real sprint work. We did it in our spare time or during company hackathons. And during this process, we chose the website’s technologies. We started with NextJS, Formik, and several other common web development libraries that were familiar to us.
In short, this effort was a huge success. We presented it to the company at an all hands and they were thrilled at the possibilities. Over the next few months, we ended up drastically shifting company priorities to focus on rebuilding the existing product on this new web platform. Eventually, we revisited our technology choices, reviewing different options with the tech leaders in our company before deciding on what would be used going forward. However, this review was focused on the primary web framework we would use. Would it be NextJS or something else? We did not review each library used, just the primary framework.
We ended up sticking with NextJS for many reasons. We also kept Formik (and most of our other libraries). So, I wouldn’t exactly say that our choice to use Formik was well thought out. We chose it originally because it was popular, familiar, and seemingly battle tested. Looking back, I do think it was a good choice. However, I’m not sure that it has aged well.
Originally, the product was small and targeted. We only had a few people working on it and the feature set was not too large. Using Formik was also easy and reliable, as we’d hoped. I do think that it was a good choice for us at that time and that, in general, it is a valuable tool. I don’t think Formik sucks. However, that project and the team involved with it have grown and now we’re running into problems caused, in part, by our usage of Formik.
One of the things that happens as a React application grows is you add more components. As your feature set grows and things get more complex, there’s a natural desire to break it down into smaller pieces that are easier to reason about, test, reuse, and so on. In the Laurel web application we did this everywhere, including in some of the more complicated forms. The problem that resulted from this was that we started directly accessing and manipulating Formik state within these forms so that different form components could change their state based on these values. We ended up with a fairly deep tree of components that were all interacting with Formik state as well as general application state.
This was ok for a little while, but eventually we encountered production incidents. Customers on less powerful machines were reporting performance issues and general difficulty in using the application. We discovered that when users were typing in a certain form it was triggering a large number of state updates throughout the form and the application.
This was a problem created by us. Formik is not at fault, we just used it incorrectly. But it made me wonder about how we got to that place - how did we naturally grow our application into a place where we had cascading state updates triggered by our form? Why was it not more clear to our team, during development, how to best use (or not use) Formik for this use case?
Certainly additional Formik documentation may have helped here. Examples similar to our use case, FAQs, etc may have been valuable for my team. However, there are many problems with relying on this as a solution. First, Formik is an open source library. They have no incentive to ensure that the library is used correctly .by developers at all skill levels. It is the company’s responsibility to either hire good developers or educate them. Second, even excellent developers don’t always read the documentation.
In recent years, other products have emerged which have their own ways of solving the “form state” problem. Many of them seem to be either trying to get closer to the behaviors of standard HTML forms (Remix) or just by building something similar to Formik that has slightly different features or more ease of use.
I don’t think that the problem we encountered at Laurel is easily solved. If you’re working with React and using a form state management library like Formik, you’ll always be relying on the skill of your developers to avoid the issues caused by additional code complexity. The form state isn’t handled by default anywhere when you start adding layers. You’ll end up managing that yourself and it’s very easy to incrementally move away from the correct path.
Moving forward, I believe tools like Remix have the right idea. The management of form state is built in to the framework and more closely mimics the standard HTML <form>
behaviors. This sort of tool makes it much harder to accidentally stray from the correct way of doing things.
At Laurel, we don’t have the option of transitioning from NextJS to Remix but learning about how Remix uses forms and reviewing our own mistakes with form building has taught us valuable lessons. We’re better developers because of it.