DEV Community

Cover image for A React Material UI Library to Build Any Form With Only JSON Config
Jeremy
Jeremy

Posted on

A React Material UI Library to Build Any Form With Only JSON Config

Suppose we want to create a form to collect shipping address. Here's how we can replicate the form below with just JSON config and the open-source library I built @jeremyling/react-material-ui-form-builder.

The JSON Config

const fields = [
  {
    title: "Shipping Address",
    component: "display-text",
    titleProps: {
      style: {
        fontSize: 20,
        marginTop: 16,
        fontWeight: "bold",
        userSelect: "none",
        fontVariant: "small-caps",
        marginBottom: 8,
      },
    },
  },
  {
    col: {
      xs: 6,
    },
    label: "First Name",
    props: {
      required: true,
    },
    attribute: "firstName",
    component: "text-field",
    validations: {
      label: "First Name",
      required: "Required",
    },
    validationType: "string",
  },
  {
    col: {
      xs: 6,
    },
    label: "Last Name",
    props: {
      required: true,
    },
    attribute: "lastName",
    component: "text-field",
    validations: {
      label: "Last Name",
      required: "Required",
    },
    validationType: "string",
  },
  {
    label: "Address Line 1",
    props: {
      required: true,
    },
    attribute: "address1",
    component: "text-field",
    validations: {
      label: "Address 1",
      required: "Required",
    },
    validationType: "string",
  },
  {
    label: "Address Line 2",
    attribute: "address2",
    component: "text-field",
    validations: {
      label: "Address 2",
    },
    validationType: "string",
  },
  {
    col: {
      xs: 6,
    },
    label: "City",
    attribute: "city",
    component: "text-field",
    validations: {
      label: "City",
    },
    validationType: "string",
  },
  {
    col: {
      xs: 6,
    },
    label: "State",
    attribute: "state",
    component: "text-field",
    validations: {
      label: "State",
    },
    validationType: "string",
  },
  {
    col: {
      xs: 6,
    },
    label: "Postcode",
    props: {
      required: true,
    },
    attribute: "postcode",
    component: "text-field",
    validations: {
      label: "Postcode",
      required: "Required",
    },
    validationType: "string",
  },
  {
    col: {
      xs: 6,
    },
    label: "Country",
    props: {
      required: true,
    },
    attribute: "country",
    component: "text-field",
    validations: {
      label: "Country",
      required: "Required",
    },
    validationType: "string",
  },
  {
    options: [
      {
        label: "Same as Billing",
        value: true,
      },
    ],
    optionConfig: {
      key: "label",
      label: "label",
      value: "value",
    },
    attribute: "sameAsBilling",
    component: "checkbox-group",
  },
];
Enter fullscreen mode Exit fullscreen mode

Standard Code For All Forms

import React, { useRef, useState } from "react";
import { FormBuilder } from "@jeremyling/react-material-ui-form-builder";
import { Button } from "@mui/material";
import { get, isEmpty } from "lodash";

async function validate(refs, form) {
  var errors = {};
  for (const [attribute, ref] of Object.entries(refs.current)) {
    if (ref.validate) {
      const error = await ref.validate(get(form, attribute));
      if (error.length) {
        errors[attribute] = error;
      }
    }
  }
  if (!isEmpty(errors)) {
    console.log(errors);
    return false;
  }
  return true;
}

export default function ShippingAddress() {
  const [form, setForm] = useState({});

  const refs = useRef({});

  const updateForm = (updates) => {
    const copy = { ...form };
    for (const [key, value] of Object.entries(updates)) {
      copy[key] = value;
    }
    setForm(copy);
  };

  const handleNext = async (event) => {
    event.preventDefault();
    const ok = await validate(refs, form);
    if (!ok) {
      return;
    }
    console.log(form);
  };

  return (
    <>
      <FormBuilder
        fields={fields}
        form={form}
        updateForm={updateForm}
        refs={refs}
      />
      <Button
        type="submit"
        variant="contained"
        color="primary"
        sx={{ mt: 1 }}
        onClick={handleNext}
      >
        Next
      </Button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation

Here, we only use a few components from the library, display-text, text-field and checkbox-group. There are many more you can use. You can view the documentation here. Just note, in general, all input components would have these core field props.

{
  ...
  title: "State", // Display text above the input
  label: "State", // Input label
  attribute: "state", // Attribute of the form to set/control
  component: "text-field", // Type of component
  col: {
    // Breakpoints and corresponding width (1 - 12)
    xs: 6,
    sm: 6,
    ...
  },
  validations: {
    // Any validations accepted by yup
    required: true,
    length: 10,
    min: 5,
    max: 20,
    matches: ['/[a-z]/i', 'Can only contain letters'],
    email: true,
    url: true,
    uuid: true,
  },
}
Enter fullscreen mode Exit fullscreen mode

Concluding Remarks

I actually created the form above with 3 clicks and a few renames on FormBlob. Try it out and make building forms a breeze. If you're a developer, contact us and I'll turn on a feature for you to export the forms you build into your own project. Alternatively, you can even build and deploy a hosted form entirely on FormBlob and embed the form in your own domain. If you use this method, you don’t have to use React in your app and it still works out of the box!

If data privacy is a concern, you can define webhooks to call on each submission to pass the form data to your own backend. If you choose, FormBlob does not store any data on our servers beyond the form structure.

Discussion (0)