Published: February 08 2020

React + Formik 2 - Form Validation Example

Example built with React 16.12 and Formik 2.1.4

Other versions available:

This is a quick example of how to setup form validation in React with Formik version 2. Formik 2 contains a higher order component that helps with managing react state, validation, error messages and form submission. Validation is done with the Yup object schema validator which hooks into Formik via the handy validationSchema prop.

The example is a simple registration form with pretty standard fields for title, first name, last name, email, password, confirm password and an accept Ts & Cs checkbox. All fields are required including the checkbox, the email field must be a valid email address, the password field must have a min length of 6 and must match the confirm password field.

Styling of the React + Formik 2 validation example is done with Bootstrap 4.4 CSS.

Here it is in action: (See on StackBlitz at

React + Formik 2 Form Validation App Component

The app component contains an example registration form built with the <Formik /> component. The initial values of each field are set in the initialValues property. Validation rules and error messages are set in the validationSchema property. The onSubmit property contains a function that gets called when the form is submitted and valid. The html and jsx markup for the form is set in callback function contained within the <Formik>...</Formik> component tag.

The <Field /> and <ErrorMessage /> components are part of the Formik library that automatically hook up html inputs and error messages with the fields defined in Formik. For more info about the helper components available check out the Formik docs.

import React from 'react';
import { Formik, Field, Form, ErrorMessage } from 'formik';
import * as Yup from 'yup';

class App extends React.Component {
    render() {
        return (
                    title: '',
                    firstName: '',
                    lastName: '',
                    email: '',
                    password: '',
                    confirmPassword: '',
                    acceptTerms: false
                    title: Yup.string()
                        .required('Title is required'),
                    firstName: Yup.string()
                        .required('First Name is required'),
                    lastName: Yup.string()
                        .required('Last Name is required'),
                    email: Yup.string()
                        .email('Email is invalid')
                        .required('Email is required'),
                    password: Yup.string()
                        .min(6, 'Password must be at least 6 characters')
                        .required('Password is required'),
                    confirmPassword: Yup.string()
                        .oneOf([Yup.ref('password'), null], 'Passwords must match')
                        .required('Confirm Password is required'),
                    acceptTerms: Yup.bool()
                        .oneOf([true], 'Accept Ts & Cs is required')
                onSubmit={fields => {
                    alert('SUCCESS!! :-)\n\n' + JSON.stringify(fields, null, 4))
                {({ errors, status, touched }) => (
                        <div className="form-row">
                            <div class="form-group col">
                                <Field name="title" as="select" className={'form-control' + (errors.title && touched.title ? ' is-invalid' : '')}>
                                    <option value=""></option>
                                    <option value="Mr">Mr</option>
                                    <option value="Mrs">Mrs</option>
                                    <option value="Miss">Miss</option>
                                    <option value="Ms">Ms</option>
                                <ErrorMessage name="title" component="div" className="invalid-feedback" />
                            <div className="form-group col-5">
                                <label htmlFor="firstName">First Name</label>
                                <Field name="firstName" type="text" className={'form-control' + (errors.firstName && touched.firstName ? ' is-invalid' : '')} />
                                <ErrorMessage name="firstName" component="div" className="invalid-feedback" />
                            <div className="form-group col-5">
                                <label htmlFor="lastName">Last Name</label>
                                <Field name="lastName" type="text" className={'form-control' + (errors.lastName && touched.lastName ? ' is-invalid' : '')} />
                                <ErrorMessage name="lastName" component="div" className="invalid-feedback" />
                        <div className="form-group">
                            <label htmlFor="email">Email</label>
                            <Field name="email" type="text" className={'form-control' + ( && ? ' is-invalid' : '')} />
                            <ErrorMessage name="email" component="div" className="invalid-feedback" />
                        <div className="form-row">
                            <div className="form-group col">
                                <label htmlFor="password">Password</label>
                                <Field name="password" type="password" className={'form-control' + (errors.password && touched.password ? ' is-invalid' : '')} />
                                <ErrorMessage name="password" component="div" className="invalid-feedback" />
                            <div className="form-group col">
                                <label htmlFor="confirmPassword">Confirm Password</label>
                                <Field name="confirmPassword" type="password" className={'form-control' + (errors.confirmPassword && touched.confirmPassword ? ' is-invalid' : '')} />
                                <ErrorMessage name="confirmPassword" component="div" className="invalid-feedback" />
                        <div className="form-group form-check">
                            <Field type="checkbox" name="acceptTerms" className={'form-check-input ' + (errors.acceptTerms && touched.acceptTerms ? ' is-invalid' : '')} />
                            <label htmlFor="acceptTerms" className="form-check-label">Accept Terms & Conditions</label>
                            <ErrorMessage name="acceptTerms" component="div" className="invalid-feedback" />
                        <div className="form-group">
                            <button type="submit" className="btn btn-primary mr-2">Register</button>
                            <button type="reset" className="btn btn-secondary">Reset</button>

export { App };

React + Formik 2 Form Validation Index HTML File

The base index html file contains the outer html for the validation example app.

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <title>React + Formik 2 - Form Validation Example</title>

    <!-- bootstrap css -->
    <link href="//" rel="stylesheet" />
    <div class="card m-3">
        <h5 class="card-header">React + Formik 2 - Form Validation Example</h5>
        <div class="card-body">
            <div id="app"></div>

React + Formik 2 Form Validation Main Entry File

The root index.jsx file bootstraps the react + formik tutorial application by rendering the App component into the #app div element defined in the base index html file above.

import React from 'react';
import { render } from 'react-dom';

import { App } from './App';

    <App />,


Need Some React Help?

Search fiverr for freelance React developers.

Follow me for updates

On Twitter or RSS.

When I'm not coding...

Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!


Supported by