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:
- Your application requires a lot of forms
- Depending on the user locale you show or not items in the forms
- The user data you need to get depends on some configuration such as role, type of user, etc
- 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:
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:
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!
Top comments (1)
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 ?