React forms can be lengthy and complicated. Likewise, most tools that assist with building forms in React often perform an excessive amount of “magic” underneath the hood, which tends to hinder performance to some extent. Formik is a small JavaScript library built in response to the hardships experienced when creating large forms in React.
In this adoption guide, we’ll discuss Formik’s features and use cases that make it a great choice for React projects with forms. We’ll also take a look at how to get started with Formik, including styling and animating it, and when an alternate form library might be better suited to your needs.
By the end of this guide, you should have a strong sense of when and how to adopt Formik effectively. Let’s get right into it.
Jared Palmer created Formik out of his frustration from building React forms while building an extensive internal administrative dashboard alongside Eon White. Faced with about 30 distinct forms, he quickly realized there was a significant advantage in standardizing input components and streamlining the data flow through the forms.
When creating Formik, Palmer’s goal was to develop a scalable, performant, form helper with a minimal API that does the “annoying stuff” and leaves the rest up to the developer.
Formik helps developers with:
Formik tracks the state of your form and then uses props to expose it to your form along with a few reusable methods and event handlers, including handleChange
, handleBlur
, and handleSubmit
. As expected, handleChange
and handleBlur
determine which field has to be updated based on an id
or name
attribute.
Quoting directly from the author, “Formik initially started by expanding on this little higher-order component by Brent Jackson, some naming conventions from Redux-Form, and (most recently) the render props approach popularized by React-Motion and React-Router 4.”
Formik has become widely accepted in the industry as one of the best form-building tools. In this section we’ll see why that’s the case and hopefully make a strong argument for why you should try out Formik in your next project.
Within the React ecosystem, Formik is a well-liked module that makes form development and maintenance easier. It simplifies creating and managing complicated forms in React apps by offering a set of tools and patterns to assist with form state, validation, and submission.
Some of Formik’s advantages include:
Despite all this, it would be naive to think these advantages automatically make Formik the best choice in any situation. You should also consider when not to use Formik for reasons like the following:
useState
and useEffect
Hooks to manage form state and validation manually may be simpler and require less codeLet’s take a quick look at how to get started with Formik next.
We’ll use create-react-app
for this simple example:
npx create-react-app my-app
To start using Formik, run this command:
npm i formik //or yarn add formik
We’ll build a simple form with two fields — username and password — and a Submit button that alerts the content of our form.
Delete the initial files in the my-app
folder created by create-react-app
and then replace it with the following files. First, this index.js
file:
##index.js import React from "react" import ReactDOM from "react-dom/client" import "./index.css" const SimpleForm = () => { return ( <div className="container"> <form id="simple-form"> <div className="form-group"> <label htmlFor="email">email:</label> <input type="text" id="email" name="email" /> </div> <div className="form-group"> <label htmlFor="password">Password:</label> <input type="password" id="password" name="password" /> </div> <button type="submit">Submit</button> </form> </div> ) } function App() { return <SimpleForm /> } const rootElement = ReactDOM.createRoot(document.getElementById("root")) rootElement.render(<App />) export default SimpleForm
Second, this index.css
file:
/* index.css */ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } /* SimpleForm.css */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: Arial, sans-serif; background-color: #f0f0f0; display: flex; justify-content: center; align-items: center; height: 100vh; } .container { background-color: #ffffff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } form { display: flex; flex-direction: column; } .form-group { margin-bottom: 1rem; } label { font-weight: bold; margin-bottom: 0.5rem; } input[type="text"], input[type="password"] { padding: 0.5rem; font-size: 1rem; border: 1px solid #ccc; border-radius: 4px; } button { padding: 0.75rem 1.5rem; font-size: 1rem; background-color: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer; transition: background-color 0.3s ease; } button:hover { background-color: #0056b3; }
The boilerplate code above is for a basic React form. We’ll apply gradual changes to the form using Formik to clearly understand how Formik works.
Import the useFormik
Hook from the formik
package and use it to set the initial values of the fields in our form:
class="language-javascript hljs"import { useFormik } from "formik" //at the beginning of the simpleForm function const formik = useFormik({ initialValues: { email: "", password: "", }, onSubmit: (values) => { alert(JSON.stringify(values, null, 2)) }, })
Now we can use the object returned from the useFormik
Hook in our form as follows:
<form id="simple-form" onSubmit={formik.handleSubmit}> <div className="form-group"> <label htmlFor="email">email:</label> <input type="text" id="email" name="email" onChange={formik.handleChange} value={formik.values.email} /> </div> <div className="form-group"> <label htmlFor="password">Password:</label> <input type="password" id="password" name="password" onChange={formik.handleChange} value={formik.values.password} /> </div> <button type="submit">Submit</button> </form>
Formik provides us with the formik.handleChange
and formik.handleSubmit
helper methods, which function as you’d expect. The formik.values
field contains all the values in our form as set in the useFormik
Hook.
With the above code, we created a simple form that has two fields and a Submit button.
In this section, we’ll continue building on our form while exploring more Formik features.
The first Formik feature we’ll look at is the formik.values
field we saw briefly in the previous section.
The formik.values
field helps track the state of our application. We can set the initial values of the fields in our application by using the useFormik
Hook and passing an initialValues
object.
The form we built above lacks any means for validating user input. Let’s see how we can add validation to our form.
Formik provides a concise method to handle user input validation by using the validate
function of the useFormik
Hook. The values of each of our fields are passed to the validate function, with which we can validate inputs.
Add the following code to the top of the simpleForm
function:
const validate = (values) => { const errors = {} if (!values.email) { errors.email = "Email is Required" } else if ( !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email) ) { errors.email = "Invalid email address" } if (!values.password) { errors.password = "Password is Required" } else if (values.password.length < 6) errors.password = "Password must contain 6 or more characters" return errors }
Then add the validate function to the useFormik
Hook:
const formik = useFormik({ //... validate, //.. })
Great! With that, we’ve added validation for user inputs in the username and password fields. If the username field is blank or contains an invalid email address, Formik will return an errors
object. This will also happen if the password field is blank or the password isn’t long enough.
The validate
function we wrote above returns an errors
object. The errors
object, which is available to us via formik.errors
, contains the error messages we set for each field in the validate function.
We can now update our form fields to display the errors as follows:
<div className="container"> <form id="simple-form" onSubmit={formik.handleSubmit}> <div className="form-group"> <!-- code left as it was --> {formik.errors.email ? ( <div> {formik.errors.email} </div> ) : null} </div> <div className="form-group"> <-- code left as it was --> {formik.errors.password ? ( <div> {formik.errors.password} </div> ) : null} </div> <button type="submit">Submit</button> </form> </div>
Formik provides a way to track fields that the user has visited previously. formik.touched
contains all the fields in our form with a boolean value set to true
if the user has visited the field. We use formik.handleBlur
handler to update the touched fields.
We can update our form to track the visited fields and display errors only if the user has visited a field:
<div className="container"> <form id="simple-form" onSubmit={formik.handleSubmit}> <div className="form-group"> <label htmlFor="email">email:</label> <input type="text" id="email" name="email" onChange={formik.handleChange} onBlur={formik.handleBlur} //track touch field value={formik.values.email} /> <!-- update to show errors only if field has been visited --> {formik.touched.email && formik.errors.email ? ( <div> {formik.errors.email} </div> ) : null} </div> <div className="form-group"> <label htmlFor="password">Password:</label> <input type="password" id="password" name="password" onChange={formik.handleChange} onBlur={formik.handleBlur} //track touched field value={formik.values.password} /> <!-- update to show errors only if field has been visited --> {formik.touched.password && formik.errors.password ? ( <div> {formik.errors.password} </div> ) : null} </div> <button type="submit">Submit</button> </form> </div>
Now that we understand how Formik works, we can take a look at some use cases:
In this section, we’ll focus on different ways we can style our Formik forms, including traditional CSS, CSS-in-JS libraries, and CSS frameworks. Here’s an example using CSS modules:
import React from 'react'; import { Formik, Form, Field, ErrorMessage } from 'formik'; import * as Yup from 'yup'; import styles from './LoginForm.module.css'; const LoginForm = () => { return ( <Formik initialValues={{ email: '', password: '' }} validationSchema={Yup.object({ email: Yup.string().email('Invalid email address').required('Required'), password: Yup.string().required('Required'), })} onSubmit={(values, { setSubmitting }) => { setTimeout(() => { console.log(values); setSubmitting(false); }, 400); }} > <Form className={styles.form}> <div className={styles.fieldContainer}> <label htmlFor="email">Email</label> <Field name="email" type="email" className={styles.field} /> <ErrorMessage name="email" component="div" className={styles.error} /> </div> <div className={styles.fieldContainer}> <label htmlFor="password">Password</label> <Field name="password" type="password" className={styles.field} /> <ErrorMessage name="password" component="div" className={styles.error} /> </div> <button type="submit" className={styles.submitButton}>Submit</button> </Form> </Formik> ); }; export default LoginForm;
Meanwhile, here’s an example using the styled-components library:
import React from 'react'; import { Formik, Form, Field, ErrorMessage } from 'formik'; import * as Yup from 'yup'; import styled from 'styled-components'; const StyledForm = styled(Form)` display: flex; flex-direction: column; `; const StyledField = styled(Field)` margin: 0.5rem 0; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; `; const StyledErrorMessage = styled.div` color: red; font-size: 0.8rem; `; const SubmitButton = styled.button` padding: 0.5rem; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; &:hover { background-color: #0056b3; } `; const LoginForm = () => { return ( <Formik initialValues={{ email: '', password: '' }} validationSchema={Yup.object({ email: Yup.string().email('Invalid email address').required('Required'), password: Yup.string().required('Required'), })} onSubmit={(values, { setSubmitting }) => { setTimeout(() => { console.log(values); setSubmitting(false); }, 400); }} > <StyledForm> <label htmlFor="email">Email</label> <StyledField name="email" type="email" /> <StyledErrorMessage><ErrorMessage name="email" /></StyledErrorMessage> <label htmlFor="password">Password</label> <StyledField name="password" type="password" /> <StyledErrorMessage><ErrorMessage name="password" /></StyledErrorMessage> <SubmitButton type="submit">Submit</SubmitButton> </StyledForm> </Formik> ); }; export default LoginForm;
You can also style Formik forms using Tailwind CSS. The code below shows how to do so:
import React from 'react'; import { Formik, Form, Field, ErrorMessage } from 'formik'; import * as Yup from 'yup'; const LoginForm = () => { return ( <Formik initialValues={{ email: '', password: '' }} validationSchema={Yup.object({ email: Yup.string().email('Invalid email address').required('Required'), password: Yup.string().required('Required'), })} onSubmit={(values, { setSubmitting }) => { setTimeout(() => { console.log(values); setSubmitting(false); }, 400); }} > <Form className="flex flex-col space-y-4"> <div className="flex flex-col"> <label htmlFor="email" className="mb-1">Email</label> <Field name="email" type="email" className="p-2 border border-gray-300 rounded" /> <ErrorMessage name="email" component="div" className="text-red-500 text-sm mt-1" /> </div> <div className="flex flex-col"> <label htmlFor="password" className="mb-1">Password</label> <Field name="password" type="password" className="p-2 border border-gray-300 rounded" /> <ErrorMessage name="password" component="div" className="text-red-500 text-sm mt-1" /> </div> <button type="submit" className="p-2 bg-blue-500 text-white rounded hover:bg-blue-600">Submit</button> </Form> </Formik> ); }; export default LoginForm;
You can also style Formik forms with a couple of third-party libraries. We’ll see some of them below. In this example, we use Formik with Material UI’s TextField
and Button
components:
import React from 'react'; import ReactDOM from 'react-dom'; import { useFormik } from 'formik'; import * as yup from 'yup'; import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; const validationSchema = yup.object({ email: yup .string('Enter your email') .email('Enter a valid email') .required('Email is required'), password: yup .string('Enter your password') .min(8, 'Password should be of minimum 8 characters length') .required('Password is required'), }); const WithMaterialUI = () => { const formik = useFormik({ initialValues: { email: '[email protected]', password: 'foobar', }, validationSchema: validationSchema, onSubmit: (values) => { alert(JSON.stringify(values, null, 2)); }, }); return ( < div > < form onSubmit = { formik.handleSubmit } > < TextField fullWidth id = "email" name = "email" label = "Email" value = { formik.values.email } onChange = { formik.handleChange } onBlur = { formik.handleBlur } error = { formik.touched.email && Boolean(formik.errors.email) } helperText = { formik.touched.email && formik.errors.email } /> < TextField fullWidth id = "password" name = "password" label = "Password" type = "password" value = { formik.values.password } onChange = { formik.handleChange } onBlur = { formik.handleBlur } error = { formik.touched.password && Boolean(formik.errors.password) } helperText = { formik.touched.password && formik.errors.password } /> < Button color = "primary" variant = "contained" fullWidth type = "submit" > Submit < /Button> < /form> < /div> ); }; ReactDOM.render( < WithMaterialUI / > , document.getElementById('root'));
The integration with Material UI allows us to use Material UI components and offers more granular control over validation, highlighting, and error display.
Similarly, we can use Formik with Semantic UI 2.0 like so:
import React from 'react'; import { Formik } from 'formik'; import { Form, Input, SubmitButton, ResetButton } from 'formik-semantic-ui-react'; export const LoginForm = (props: any) => { const initialValues = { email: '', password: '', }; return ( <div> <Formik initialValues={initialValues} onSubmit={ ()=>{ console.log('Form Submit' )} } > <Form size="large"> <Input name="email" placeholder="Email" errorPrompt /> <Input name="password" type="password" placeholder="Password" errorPrompt /> <SubmitButton fluid primary> Login </SubmitButton> <ResetButton fluid secondary> Reset </ResetButton> </Form> </Formik> </div> ); };
We used Semantic UI components like Form
, Input
, SubmitButton
, and ResetButton
. This example using Semantic UI with Formik shows how simple it is to integrate.
Many other third-party UI integrations are possible in Formik. You can check the Formik documentation directly for more details.
Let’s compare Formik to tools such as React Hook Form, Informed, react-ts-form, and traditional form management in React. The comparison will consider points such as features, performance, community, and documentation to help inform your choice:
Formik | React Hook Form | Informed | react-ts-form | |
---|---|---|---|---|
Features | – State management: Keeps track of values, errors, and touched fields – Validation: Integrates with validation libraries; supports synchronous and asynchronous validation – UI libraries – Multi-step forms |
– State management: Uses uncontrolled components and ref-based API for minimal re-renders – Validation: Has built-in support using native HTML validation, custom validation, and integration with validation libraries – UI libraries – Multi-step forms: Recommended to use a state library to store user inputs |
– State management: Has a simpler API, including values, touched fields, errors, and dirty fields – Validation: Supports built-in validation, custom validation, and schema-based validation using Yup – Multi-step forms |
– Type safety: Strongly typed forms with TypeScript support – State management: With a focus on TypeScript integration – Validation: Built-in validation with support for schema-based validation using libraries like Yup – Form control: Granular control over form state and behavior, with a focus on type safety |
Performance | Controlled components can lead to more frequent re-renders. Managing form state internally can also be less performant for large forms. Handles complex form logic well, but may introduce performance overhead for extensive forms | Highly optimized for performance with minimal re-renders and subscriptions. Also handles complex forms efficiently with better performance for large forms due to its architecture | Efficient approach to state management minimizes re-renders. Optimized with better handling of large forms and complex states, focusing on performance and ease of use | Built to work efficiently with TypeScript, ensuring minimal re-renders and type safety. Optimized for performance with better handling of large forms and complex states |
Community | Widely used and established in the React community. Actively maintained GitHub repo with many contributors, posts, and articles | Gaining rapid popularity due to its performance benefits and modern approach. Active GitHub repo with many contributors, posts, and articles | Gaining traction but not as widely adopted as Formik. Smaller community, but still active GitHub repo | Gaining traction, especially among TypeScript users. Smaller but growing community, with an active GitHub repo and contributors |
Resources | – Documentation: Comprehensive and detailed, with examples and use cases – Tutorials: Numerous online tutorials, guides, and example projects – Official support: Maintained by the Formik team, which provides regular updates and improvements |
– Documentation: Extensive and well-organized, with clear examples and API references – Tutorials: Plenty of tutorials, guides, and example projects available online – Official support: Actively maintained with regular updates, improvements, and community engagement |
– Documentation: Clear and well-organized, with examples and API references – Tutorials: Fewer tutorials and example projects compared to Formik, but still sufficient for most use cases – Official support: Actively maintained with regular updates and enhancements |
– Documentation: Clear and well-organized, with examples and API references, particularly for TypeScript users – Tutorials: Fewer tutorials and example projects compared to Formik, but sufficient for most projects – Official support: Actively maintained with regular updates and enhancements. |
This comparison table should serve as an efficient way to evaluate these form handling options at a high level. Keep in mind there are many other options you could consider as well; this is just a sample of some of the most popular.
Formik offers a reliable solution for managing forms of any size and complexity. Its comprehensive state management, smooth validation integration, and support for multi-step forms make it a performant and developer-friendly solution for React forms.
Using Formik in your React apps will significantly improve the way you handle forms, which will improve the productivity of your development process and the maintainability of your codebase.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowDesign React Native UIs that look great on any device by using adaptive layouts, responsive scaling, and platform-specific tools.
Angular’s two-way data binding has evolved with signals, offering improved performance, simpler syntax, and better type inference.
Fix sticky positioning issues in CSS, from missing offsets to overflow conflicts in flex, grid, and container height constraints.
From basic syntax and advanced techniques to practical applications and error handling, here’s how to use node-cron.