DEV Community

loading...

Autogenerate Forms from JSON using React

adancarrasco profile image Adán Carrasco ・3 min read

TLDR; Please take me to the code sample

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:

And optionally we can add:

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

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
Input field hover

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.

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...

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

Creating an example

Let's start by creating one example:

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 where I added Redux and also used the @reduxjs/toolkit

Thanks for reading!

Discussion

pic
Editor guide
Collapse
fatmaghorbel profile image
Fatmaghorbel

thank you for this article! Do you have any link where we would add a backend part !
I have another question , how can we generate a form automatically when I run a URL after building the project ? , I want to have a zip file contains the form generated each time ?