Custom components with Formik

I want to thank codevolution for his amazing work in general, and more specifically for his playlist on Formik. More specifically these articles deals with the video of the playlist, starting at video 31. The original source code can be found on github.

FormikContainer component

A Formik wrapper that is used to test the FormikControl component. Here is the skeleton:

import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
const FormikContainer = () => {
  const initialValues = {};
  const validationSchema = Yup.object({});
  const onSubmit = (values) => console.log('Form data', values);

  return (
    <Formik
      initialValues={initialValues}
      validationSchema={validationSchema}
      onSubmit={onSubmit}
    >
      {(formik) => (
        <Form>
          <button type='submit'>Submit</button>
        </Form>
      )}
    </Formik>
  );
};

export default FormikContainer;

FormikControl component

A common Formik Control component that can render the different types of form elements. The skeleton is:

import React from 'react';
const FormikControl = (props) => {
  
const { control, ...rest } = props
  switch (control) {
    case 'input':
    case 'textarea':
    case 'select':
    case 'radio':
    case 'checkbox':
    case 'date':
    case 'chakraInput':
    default:
      return null;
  }
};

Input

It is defined by 3 pieces of information:

  • the label of the input field

  • the formik Field component

  • the ErrorMessage component

The following props are used:

  • control='input'

  • label='Email'

  • name='email'

  • type='email'

Let's create Input.js:

import React from 'react'
import { Field, ErrorMessage } from 'formik'
import TextError from './TextError'
function Input (props) {
  const { label, name, ...rest } = props
  return (
    <div className='form-control'>
      <label htmlFor={name}>{label}</label>
      <Field id={name} name={name} {...rest} />
      <ErrorMessage component={TextError} name={name} />
    </div>
  )
}
export default Input

With TextError.js:

import React from 'react'
function TextError (props) {
  return <div className='error'>{props.children}</div>
}
export default TextError

Next wire the new component in the FormikControl component:

import Input from './Input'
...
case 'input':
      return <Input {...rest} />
...
Next test the new input component with the FormikContainer:

import FormikControl from './FormikControl'
...
const initialValues = {    email: '' }
const validationSchema = Yup.object({
    email: Yup.string().required('Required'),
})
...
<FormikControl
            control='input'
            type='email'
            label='Email'
            name='email'
/>

TextArea

In the container, declare initial value and validation schema:

const initialValues = { email: '', description: '' };
  const validationSchema = Yup.object({
    email: Yup.string().required('Required'),
    description: Yup.string().required('Required'),
  });

Then create the appropriate FormikControl component:

<FormikControl
 control='textarea'
 label='Description'
 name='description'
/>

In the FormikControl component implement the case where control equals textarea:

case 'textarea':
 return <Textarea {...rest} />;

Finally implements Textarea itself, don't forget as='textarea':

import React from 'react';
import { Field, ErrorMessage } from 'formik';
import TextError from './TextError';
function Textarea(props) {
  const { label, name, ...rest } = props;
  return (
    <div className='form-control'>
      <label htmlFor={name}>{label}</label>
      <Field as='textarea' id={name} name={name} {...rest} />
      <ErrorMessage component={TextError} name={name} />
    </div>
  );
}
export default Textarea;

Select

In the container, declare the drop down values, declare the initial state value and the validation schema:

const dropdownOptions = [
    { key: 'Select an option', value: '' },
    { key: 'Option 1', value: 'option1' },
    { key: 'Option 2', value: 'option2' },
    { key: 'Option 3', value: 'option3' }
]
const initialValues = {
    selectOption: '',
}
const validationSchema = Yup.object({
  selectOption: Yup.string().required('Required'),
})

Then create the appropriate FormikControl component:

<FormikControl
  control='select'
  label='Select a topic'
  name='selectOption'
  options={dropdownOptions}
/>

In the FormikControl component implement the case where control equals

select
:

case 'select':
 return <Select {...rest} />;

Finally implement the component Select itself, don't forget as='select' and the options as the children of the Field components:

import React from 'react';
import { Field, ErrorMessage } from 'formik';
import TextError from './TextError';
function Select(props) {
  const { label, name, options, ...rest } = props;
  return (
    <div className='form-control'>
      <label htmlFor={name}>{label}</label>
      <Field as='select' id={name} name={name} {...rest}>
        {options.map((option) => {
          return (
            <option key={option.value} value={option.value}>
              {option.key}
            </option>
          );
        })}
      </Field>
      <ErrorMessage component={TextError} name={name} />
    </div>
  );
}
export default Select;

ReadioButtons

Checkboxes

DatePicker

User Login and Registration form

A course Enrollment Form

Integrate a UI Component library