I decided to revisit and revamp some of my full stack applications. I realized that I had two features that I never added to my Seize The Day app, so I'm quickly knocking them out. While implementing the Subtasks feature for the app, I had to do some digging about integrating Formik and Material UI components together. One specific challenge was integrating a Formik <FieldArray> and <Field> component with a Material UI <TextField> component, and there were multiple resources that I had to tie together to get the job done. So I decided to piece together this blog post in hopes that someone in the future only has to dig for this one resource!
TLDR: Formik's <Field> component accepts an "as" property, to which you can pass an entire other component for <Field> to be compiled as, and yet still have Formik's functionality.
Getting Started
To preface, I have a <CreateTaskButton> component which is a button that when clicked, opens a modal to reveal a form. Before this feature, users could add a task to their “to-do” list by simply entering a “title” for their task. In the gif below, you can see that the “subtasks” and “tags” features are disabled. When the user is finished, clicking the “Create New Task” button in the bottom right corner of the entire modal submits the form. All of the values from the inputs are collected together and passed off to a createTodo function.
![A gif visualizing the functionality of the Seize The Day app. A user clicks on "Create New Task" to show a form with an active input field of "title". The user then submits this new task by clicking on the "create new task" button.](https://static.wixstatic.com/media/85b4f0_51efa7a0cd784b58aaa71cc8efd6b6fb~mv2.gif/v1/fill/w_600,h_288,al_c,pstr/85b4f0_51efa7a0cd784b58aaa71cc8efd6b6fb~mv2.gif)
This new Subtasks feature allows users to have the option of adding "subtasks" to their new task. For example, a task's title could be "Grocery List" and the accompanying subtasks could be "Apple", "Orange", etc.
![](https://static.wixstatic.com/media/85b4f0_4e8e3d58b2494f5e87138eb00789dcf0~mv2.gif/v1/fill/w_980,h_479,al_c,usm_0.66_1.00_0.01,pstr/85b4f0_4e8e3d58b2494f5e87138eb00789dcf0~mv2.gif)
One of the challenges of building this feature was figuring out how to build the logic using the Formik library. For the original build of the app, I chose to utilize Formik because of its form control. Instead of writing your own form logic, such as “handleSubmit” for form submission and “handleChange” for form inputs, Formik handles a form’s functionality ~behind the scenes~.
Initial Setup for Formik
At form submission, Formik sends off values which is an object that holds the form’s input values.
![](https://static.wixstatic.com/media/85b4f0_61d48f49d87d4f48a104fa2267fdd5bc~mv2.png/v1/fill/w_776,h_443,al_c,q_85,enc_avif,quality_auto/85b4f0_61d48f49d87d4f48a104fa2267fdd5bc~mv2.png)
Formik requires the shape of the form to be defined in the object initialValues. Originally, I told Formik to expect a “title” who’s value would be a string.
![](https://static.wixstatic.com/media/85b4f0_74eb855a9c504bb58e2340f64e753c95~mv2.png/v1/fill/w_980,h_503,al_c,q_90,usm_0.66_1.00_0.01,enc_avif,quality_auto/85b4f0_74eb855a9c504bb58e2340f64e753c95~mv2.png)
Now, I needed to tell Formik to expect subTasks whose value would be an array of objects, with each object representing a single subtask.
![](https://static.wixstatic.com/media/85b4f0_dccc4d7b4a044df19eb853d873d11817~mv2.png/v1/fill/w_980,h_528,al_c,q_90,usm_0.66_1.00_0.01,enc_avif,quality_auto/85b4f0_dccc4d7b4a044df19eb853d873d11817~mv2.png)
Building the input components for the form
For Formik to keep track of a string that is changing in an input, it’s as simple as including onChange={handleChange} and value={values.KEY_NAME} in a component. The name property's value should match the key in the values object. This tells Formik that as the input changes (onChange), to update the corresponding property in the values object (name) to be the new input's value (value). All of this magic happens for every keystroke in an input, so if a user were to type "App" in the input, Formik would be updating the values object each time for "A", "p", and "p".
![](https://static.wixstatic.com/media/85b4f0_6ae9fcf0722944c2b0bf029affaf0626~mv2.png/v1/fill/w_460,h_359,al_c,q_85,enc_avif,quality_auto/85b4f0_6ae9fcf0722944c2b0bf029affaf0626~mv2.png)
For Formik to keep track of the “subTasks” array (as we defined in initialValues), this would need different logic than a simple string, as we saw in the example above.
This video showed me that for tracking the values in an array, Formik requires the <FieldArray> component. Luckily, Formik documentation has boilerplate code for setting up a <FieldArray>. It's a large image, so I won't be sharing that here. However, I will explain how within the <FieldArray> component, the practice is to map through the array, which in my case is "subTasks", and render a <Field> component (input element) for each subtask.
So now, we're not just dealing with a simple string, we're updating an array of objects. Unlike a typical onChange={handleChange} situation, as explained earlier, the <Field> component requires specific syntax for Formik to be able to track changes of the input's value. For my specific values, the name property of the Field component had to be written as name={`subTasks[${index}].title`} where index is defined per component, because we are mapping through the array.
![](https://static.wixstatic.com/media/85b4f0_14ec7763da124192a4b530f96e44d930~mv2.png/v1/fill/w_773,h_449,al_c,q_85,enc_avif,quality_auto/85b4f0_14ec7763da124192a4b530f96e44d930~mv2.png)
For updating the array's values (each subtask object), Formik has arrayHelpers, so all I had to do was create the buttons and give them an onClick property corresponding to their functionality. The “Delete” button has the arrayHelpers remove an entire subtask object from the array, and the “Add a subtask” button has the arrayHelpers push an entire new subtask object to the array, with default values of title: ”” and completed: false.
![](https://static.wixstatic.com/media/85b4f0_0bd380bdbce54c0d868a9f18ab77e81e~mv2.png/v1/fill/w_871,h_711,al_c,q_90,enc_avif,quality_auto/85b4f0_0bd380bdbce54c0d868a9f18ab77e81e~mv2.png)
Great! So I implemented the <FieldArray> component with <Field> components being rendered for each "subtask". But this is what the code was rendering to the user interface:
![](https://static.wixstatic.com/media/85b4f0_679e837666474b869e8fe27feb8afaa7~mv2.png/v1/fill/w_546,h_441,al_c,q_85,enc_avif,quality_auto/85b4f0_679e837666474b869e8fe27feb8afaa7~mv2.png)
Yeah, let's be real. That's not cute.
Since the beginning of this app's development, I've styled my app using Material UI (MUI), a React component library. It's use is mostly simple: developers import components from MUI’s library and these components are pre-built, highly reusable, and pre-styled, providing consistency across the app. MUI's usage is noticeable in the image above: both the "Title" and "Tags" inputs are consistently styled.
We see what that looks like for the user interface, but what does that look like in the code?
For the "Title" and "Tags" inputs, I used <TextField> components from MUI. Before building out the Subtasks feature, I had already successfully integrated Formik's functionality with MUI's <TextField> component.
![](https://static.wixstatic.com/media/85b4f0_c88ee1115793445597d86f9274df86e9~mv2.png/v1/fill/w_670,h_364,al_c,q_85,enc_avif,quality_auto/85b4f0_c88ee1115793445597d86f9274df86e9~mv2.png)
Where I struggled was getting Formik’s <Field> component to comply with MUI’s <TextField> styling. The other inputs in the form were using MUI’s TextField components, but this input must use Formik’s <Field> component for it to work properly with the <FieldArray> functionality.
After searching up “Formik FieldArray with MUI”, I found a codepen that gave me my answer.
Formik’s <Field> component accepts an “as” property, where we can tell the Field component to render as a particular component. So developers can pass “select”, “textarea”, or a whole component to the “as” property and that is how the Field would be compiled into HTML code. In my case, I passed it MUI’s TextField component, and was able to treat Field as an MUI TextField, passing it the stylistic properties I needed while still having the functionality of a Formik Field.
![](https://static.wixstatic.com/media/85b4f0_b9034d84fbe145e580b465f823898564~mv2.png/v1/fill/w_676,h_680,al_c,q_90,enc_avif,quality_auto/85b4f0_b9034d84fbe145e580b465f823898564~mv2.png)
Lastly, all I had to do was create uniform styling for the buttons, and for the most part, the user interface stuff was ~done~. I was able to move on to updating the Context and API logic for creating a new task (now with subtasks!) and for updating the subtasks (toggling a subtasks' completion).
Comments