Generating Forms With React

Yekeen Ajeigbe
Stories From The Keen
3 min readDec 5, 2017

For me, building forms from scratch is a repetitive chore which is somewhat irritating because generally, HTML forms aren’t particularly reusable and more importantly, they are somewhat difficult to change easily as a project grows.

Like all problems, there are lots of JavaScript libraries to solve this for all the major libraries/frameworks but it didn’t feel right to include a library for something so trivial and unless you need something more specialised, you probably don’t need to.

Building a solution

When building a solution for this, my objectives were:

  • Pass the form definition in an array of objects with each object containing the definition for an individual field.
  • Self contained and ultra simple to modify the form.
  • Keep it semantic!

Let’s get started

Note: While these examples are written in React/JSX, the basic principles apply to almost any framework/library. I actually first created this technique over a year ago using Vue.js 😍 and have used a variation of this in the past at Formplus using Angular 1 😐

1. Define your form definition

As mentioned earlier, our form definition is a simple array of objects. Like so:

The only required property is the type which informs which component to render. Additional properties defining a wide range of behaviour can be added as needed, for example min, max props for a number field or an additional defaultValue property to an option field (selects). Your specific need should dictate the number/type of properties. I recommend however, that you only include the properties you need in each field and nothing more, to avoid bloat.

2. The Form Component

Our form component will take two props:

  • submissionHandler : A function which :smirk: handles the submission event
  • formFields : An array containing objects containing the form definition

Below is the form component:

More optional props can be added to the form component if desired, for example, a submissionText prop which can be used to change the text on the submit button which will require the button markup to be changed to something like <button type=”submit”>{submissionText || “Submit”}</button>

3. Rendering the Form Fields

The basic idea behind rendering fields is by looking at the type property on the field and rendering the matching component/block.

What the renderFormFields function is to map over each field property creating and pushing the field component (gotten from renderField(field) ) to an array which is then returned at the end of the function. The code:

The renderField function uses a switch statement on the field.type to return the appropriate field component. It uses the fall-through behaviour of switch to prevent duplicating code and if no matching/supported type is found, it throws a console.error message with null returned.

For each field type you wish to add, check for the type and return the appropriate component.

A few suggestions:

  • Omit unneeded props from the field object before spread/return is recommended to avoid passing .
  • Order your switch such that the most used fields for your application come first.
  • While some components are simple enough to be included directly in the switch as we did above, most of the time, it is best to have them in standalone component depending on your preferences.

That’s it! You now have a completely configurable, easy-to-use and manage form generator of your very own. Using this generator also allows you to do a whole lot of fun things like creating a form builder with user selecting their form components, dynamic form creation, get form definitions from an API allowing you to render different forms for different users depending on the API data and so much more.

See a complete working example on Codepen.

Hope you enjoyed this. Suggestions and constructive criticism are very much welcome.

Update 1 — In the initial renderFormFields, I was using fields.map but thanks to the great comments here, this has been refactored to fields.forEach .

--

--