React Flexible FormDocsExamplesGitHub
DocumentationOverviewGetting StartedTypeScriptAPIuseFormFielduseSubformuseFieldArrayuseConvertutilsExamplesSimple FormChakra UI FormForm with ArraySubformCustom useConvertForm State in ReduxForm with LoaderForm based on Material UIThrottled error validation

Form based on Material UI components

Example of using React Flexible Form with Material UI library. Material UI form controls have helperText property to display text under control. To pass error text to helperText custom useConvert hook is used with the <Field /> component.

"use client";
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
  OnSubmit,
  useForm,
  Field,
  ConvertHook,
  getIn,
  getInErrors,
} from "react-flexible-form";
import { useCallback, useMemo } from "react";
import TextField, { TextFieldProps } from "@mui/material/TextField";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import { yupResolver } from "react-flexible-form-resolvers";
import * as Yup from "yup";

type Values = {
  country: string;
  state: string;
  city: string;
  zipCode: string;
  street1: string;
  street2?: string | undefined;
};

const resolver = yupResolver(
  Yup.object({
    country: Yup.string().required("Required"),
    state: Yup.string().required("Required"),
    city: Yup.string().required("Required"),
    zipCode: Yup.string().required("Required"),
    street1: Yup.string().required("Required"),
    street2: Yup.string(),
  }),
);

const useConvert: ConvertHook<Values, TextFieldProps> = ({
  rffName: name,
  formControl: { values, errors, touched, setFieldValue, setFieldTouched },
}) => {
  const value = getIn({ values, name: name as any });
  const error = getInErrors({ errors, name: name as any });
  const fieldError = error.length ? error.join(", ") : undefined;
  const fieldTouched = !!getIn({ values: touched, name: name as any });

  return useMemo(() => {
    return {
      value,
      error: fieldTouched && !!fieldError,
      helperText: (fieldTouched && fieldError) || " ",
      onBlur: () => setFieldTouched(name as any, true),
      onChange: ({ target: { value } }) => setFieldValue(name as any, value),
    };
  }, [name, value, fieldError, fieldTouched, setFieldTouched, setFieldValue]);
};

const MaterialUiForm = () => {
  const onSubmit = useCallback<OnSubmit<Values>>(
    ({ formControl: { values, setIsSubmitting } }) => {
      alert(`Form values: \n ${JSON.stringify(values)}`);
      setIsSubmitting(false);
    },
    [],
  );

  const formControl = useForm({
    initialValues: {
      country: "",
      state: "",
      city: "",
      zipCode: "",
      street1: "",
      street2: "",
    },
    onSubmit,
    resolver,
  });

  return (
    <Box
      component="div"
      sx={{ display: "flex", flexDirection: "column", gap: "10px" }}
    >
      <Box component="div" sx={{ display: "flex", gap: "10px" }}>
        <Box
          component="div"
          sx={{
            flex: 1,
            display: "flex",
            flexDirection: "column",
            gap: "10px",
          }}
        >
          <Field
            rffFormControl={formControl}
            rffName="country"
            rffComponent={TextField}
            label="Country"
            rffUseConvert={useConvert}
          />
          <Field
            rffFormControl={formControl}
            rffName="state"
            rffComponent={TextField}
            label="State"
            rffUseConvert={useConvert}
          />
          <Field
            rffFormControl={formControl}
            rffName="city"
            rffComponent={TextField}
            label="City"
            rffUseConvert={useConvert}
          />
        </Box>
        <Box
          component="div"
          sx={{
            flex: 1,
            display: "flex",
            flexDirection: "column",
            gap: "10px",
          }}
        >
          <Field
            rffFormControl={formControl}
            rffName="zipCode"
            rffComponent={TextField}
            label="Zip Code"
            rffUseConvert={useConvert}
          />
          <Field
            rffFormControl={formControl}
            rffName="street1"
            rffComponent={TextField}
            label="Street Address 1"
            rffUseConvert={useConvert}
          />
          <Field
            rffFormControl={formControl}
            rffName="street2"
            rffComponent={TextField}
            label="Street Address 2"
            rffUseConvert={useConvert}
          />
        </Box>
      </Box>
      <Button
        disabled={formControl.isSubmitting}
        onClick={formControl.handleSubmit}
      >
        Submit
      </Button>
    </Box>
  );
};

export default MaterialUiForm;