DEV Community

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

Posted on

7 1

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.

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series ๐Ÿ“บ

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series ๐Ÿ‘€

Watch the Youtube series

๐Ÿ‘‹ Kindness is contagious

Please leave a โค๏ธ or a friendly comment on this post if you found it helpful!

Okay