Autogenerate Forms from JSON using React
Tags: react, javascript, beginners, webdev
Published: 2020-03-22 23:21:50

---
title: Autogenerate Forms from JSON using React
published: true
description: How to create any type of forms with React and a simple JSON file
tags: react, javascript, beginners, webdev
---
TLDR; [Please take me to the code sample](https://codesandbox.io/s/naughty-mcclintock-f0p6f)

During the last two years I have been working in different projects and the need of **creating different forms has been a common denominator**. Today I will share a way to create Forms from a JSON configuration. There are many reasons why you would need a Form generator: 

1. Your application requires a lot of forms
2. Depending on the user locale you show or not items in the forms
3. The user data you need to get depends on some configuration such as role, type of user, etc
4. Yours

For this to be possible, the generation of the forms will be based on a JSON config object that we will pass to our Form component so we create any combination of forms.

In this example I will not get deep into different input types but it should give you the basis, then you could include more types depending on the requirements. For now, this example shows how to generate forms with `<input />` in any type.

> Would be a good exercise for you to implement the `<select>` for example. 😎

Let's start by adding the libraries we are going to use:
- [react](https://reactjs.org/)
- [formik](https://jaredpalmer.com/formik/)

And optionally we can add:
- [redux](https://redux.js.org/)
- [react-redux](https://react-redux.js.org/)
- [@reduxjs/toolkit](https://redux-toolkit.js.org/)
- [@material-ui/core](https://material-ui.com/)

You can create your PoC in https://codesandbox.io

Now that we have added the libraries we start by creating a generic _FormField_ component which will be rendered by every field we want to generate.

### Form field

```JavaScript
import React from "react"
import { useField, FieldInputProps } from "formik"
import { Grid, TextField } from "@material-ui/core"

export interface FormFieldProps extends FieldInputProps<""> {
  name: string
  type: string
  label: string
}

const FormField: React.FC<FormFieldProps> = ({
  label,
  ...props
}: FormFieldProps) => {
  const [field, meta] = useField(props)
  return (
    <Grid container>
      <TextField label={label} {...field} {...props} />
      {meta.touched && meta.error ? (
        <div className="error">{meta.error}</div>
      ) : null}
    </Grid>
  )
}

export default FormField
```

This will render something like:
![Input field](https://dev-to-uploads.s3.amazonaws.com/i/k2kar7eiwjw1o6za9ggj.png)
![Input field hover](https://dev-to-uploads.s3.amazonaws.com/i/zlzci0u2s69vyml2pz3q.png)

### Generic form

Now that we have our first generic component we can create our _GenericForm_ which will render the FormField components depending on the configuration.

```JavaScript
import React from "react"
import { Form, FormikProps, Formik } from "formik"
import { Button, Box } from "@material-ui/core"

import FormField from "../FormField"
import { FormFieldProps } from "../FormField/FormField.component"

const GenericForm: React.FC<any> = props => {
  const { fields, submitClickCallback } = props.formDefinition
  return fields ? (
    <Formik
      initialValues={{}}
      onSubmit={(values, actions) => {
        submitClickCallback(values)
        actions.setSubmitting(false)
        actions.resetForm()
      }}
    >
      {(props: FormikProps<any>) => (
        <Form>
          {fields.map(({ name, type = "text", label }: FormFieldProps) => (
            <FormField key={name} name={name} type={type} label={label} />
          ))}
          <Box mt={3}>
            <Button type="submit" variant="contained" color="primary">
              Submit
            </Button>
          </Box>
        </Form>
      )}
    </Formik>
  ) : null
}

export default GenericForm
```

> In this case the button is set by default, but you could also set the number of buttons from your configuration.

### JSON data structure

Now that we have our GenericForm and our GenericField let's see how our JSON config structure would look like...

```JSON
const formDefinition = {
  fields: [
    {
      name: "firstName",
      label: "First name"
    },
    {
      name: "lastName",
      label: "Last name"
    },
    {
      name: "email",
      type: "email",
      label: "E-mail"
    }
  ],
  submitClickCallback: (values) => {
    // our callback
  }
};
```

At this point we have everything we need to generate forms based on the config file. To generate forms we would only need to render our `<Form />` component and pass the JSON object so it generates the form for us.

If we injected this JSON to our `<Form />` component the layout would look something like:

![User form](https://dev-to-uploads.s3.amazonaws.com/i/v1qcawjauiyxbuzql070.png)

### Creating an example
Let's start by creating one example:

```JavaScript
import React from "react"

import { store } from "../../App"
import Form from "../Form"

interface FormProps {
  firstName: string
  lastName: string
  email: string
}

const formDefinition = {
  fields: [
    {
      name: "firstName",
      label: "First name"
    },
    {
      name: "lastName",
      label: "Last name"
    },
    {
      name: "email",
      type: "email",
      label: "E-mail"
    }
  ],
  submitClickCallback: (values: any) => {
    store.dispatch(userSlice.actions.addUser(values))
  }
};

const UserForm = () => <Form formDefinition={formDefinition} />

export default UserForm
```

In this example we are creating a `<UserForm />` component, however this component is not required, you could have the form rendered in a Page/Screen by doing `<Form formDefinition={myJsonDefinition} />` that would be enough for you to render your form.

Then you could have some logic on your backend to return the JSON config based on the user's variables, as mentioned before: locale, different role, etc.

What else would you add to the form generator?

You can play with this project in: [Form generator](https://codesandbox.io/s/naughty-mcclintock-f0p6f) where I added [Redux](https://redux.js.org/) and also used the [@reduxjs/toolkit](https://redux-toolkit.js.org/)

Thanks for reading!