Simple structure to write forms in React with Formik

September 7, 20203 min read579 words

Table of Contents

Introduction

I wanted to share how I use Formik to write forms in my react code. I expect you've already heard and tried Formik before so we can discuss mostly component structure here. This article is not intended to be a full Formik tutorial. Formik docs has all the information you need.

useFormik hook

Out of different ways you can use Formik to create forms, the useFormik hook is the only thing I've ever needed. It takes a config object and returns an object with all the tools we need to create forms.

The minimum values you need to add in the config object are:

  1. initialValues object - Values that go inside the form fields when the form is rendered. This is the initial state of the form.

  2. onSubmit function - Receives the final values when the user submits the form. Use this function to clean up the values if needed before sending it to the server.

  3. validate function - Run validations on values and return errors. There is an alternative validationSchema which you can use to define a schema using Yup object schema validation library.

const formik = useFormik({
  initialValues,
  onSubmit,
  validate,
});

const {
  values,
  errors,
  handleSubmit,
  setFieldValue,
  ...andManyOthers
} = formik;

Component Structure

I always use the good old two-level container & presentational structure for forms.

  1. A container component where I define all the config that goes in useFormik(config) hook.
  2. A presentational component that receives this config prop and renders the form component.

This structure is a general style to write reusable components. This works even better for forms by creating separate containers for create and edit items but uses the same <Form /> component that displays the form.

For example, a user account form structure looks like this.

  1. <CreateUserFormContainer />
  2. <UpdateUserFormContainer />
  3. <UserForm />

validate function or validationSchema goes in another file and imported into form containers.

Even if you don't need both create and edit, writing a form container keeps things clear in large forms.

If we are using both forms, I also send an extra isNew prop to <UserForm /> to know whether it's a create form or an edit form. This helps to display correct error messages and other text.

A diagram for form structure

User account form

Here's the CodeSandbox Link to see the code for a sample User account form along with some utils and validations.

CodeSandbox Link

Final Thoughts

Although we've only needed the useFormik hook, if you want to create custom fields using Formik elements like <Field>, <ErrorMessage>, useFormik won't work. So, be sure to check useFormik docs to see what are you missing by using the useFormik hook.

If you find yourself writing too many conditionals in JSX to use the same <Form /> for both create and edit or there are a lot of differences in form fields in create and edit forms, it's time to separate them in different components.

You never need to store form data in a global state especially using state-management libraries like Redux or MobX.