React Material UI Stepper Form with Formik & Yup

Overview

This article will break down a step form built with the Material UI and Formik for input validation. The application will be based on an event. The form has four separate forms for user, event with the start & end dates, images, and Terms & Conditions. Screen recording of this application is playing at the bottom of this article.

Download Source from GitHub https://github.com/fullstacksoup

GitHub

Getting Started

Create the application if you want to do this from scratch. I recommend cloning the source files in case the library versions have changed.

npx create react-app multi-step-form-demo

Material UIVersion 4

React-Material for the inputs and layout.

Note: Version 5 was released during the development of this article.

For version 4 documentation click here
For the current version documentation click here
All documentation for all versions here

npm install @material-ui/core@4.12.3 @material-ui/icons@4.11.2 
npm install @material-ui/lab@4.0.0-alpha.60 
npm install @material-ui/pickers@3.3.10

Material Dropzone

material-dropzone is used for uploading with thumbnail previews of multiple files.

npm i material-ui-dropzone

Mask Phone Field

react-text-mask is the library used to create the phone mask. Example: (555) 555-5555

npm i react-text-mask

Formik & Yup

Formik and Yup are used for form validation.

npm i formik
npm i yup

Material Stepper With Formik & Yup

This will be the parent component index.js will handle the multi step form UI logic and field validation for all the forms. The stepper form is take directly from Material UI’s website here if you want more details on stepper forms then go to https://mui.com/components/steppers/#main-content.

Each form will have a Yup validation schema that will be used by the corresponding Formik

Library Imports

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { makeStyles, withStyles } from '@material-ui/core/styles';
import clsx from 'clsx';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import Check from '@material-ui/icons/Check';
import PlaylistAddCheckIcon from '@material-ui/icons/PlaylistAddCheck';
import PersonIcon from '@material-ui/icons/Person';
import EventIcon from '@material-ui/icons/Event';
import CameraIcon from '@material-ui/icons/Camera';
import StepConnector from '@material-ui/core/StepConnector';
import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import { useFormik } from 'formik';
import * as yup from 'yup';
import PersonalInfoForm from 'components/stepper-form/PersonalInfoForm';
import EventForm from 'components/stepper-form/EventForm';
import AddImageForm from 'components/stepper-form/AddImageForm';
import TermsConditionsForm from 'components/stepper-form/TermsConditionsForm';
import { format, addDays } from 'date-fns';

Inline Styles

const useStyles = makeStyles((theme) => ({
  appBar: {
    position: 'relative',
  },
  layout: {
    width: '90vw',
  },
  paper: {
    marginTop: theme.spacing(3),
    marginBottom: theme.spacing(3),
    padding: theme.spacing(2),
    [theme.breakpoints.up(600 + theme.spacing(3) * 2)]: {
      marginTop: theme.spacing(6),
      marginBottom: theme.spacing(6),
      padding: theme.spacing(3),
    },
  },
  stepper: {
    padding: theme.spacing(3, 0, 5),
  },
  buttons: {
    display: 'flex',
    justifyContent: 'flex-end',
  },
  button: {
    marginTop: theme.spacing(3),
    marginLeft: theme.spacing(1),
  },
  instructions: {
    marginTop: theme.spacing(1),
    marginBottom: theme.spacing(1),
  },
}));

Material Stepper

The code below is taken directly from Material UI documentation for the stepper form. The functions generate the style, shape, colors based on the status of the step.


function QontoStepIcon(props) {
  const classes = useStyles();
  const { active, completed } = props;
  const [ email, setEmail ] = useState('');
  const [ isEmailValid, setIsEmailValid ] = useState(false);    
  return (
    <div
      className={clsx(classes.root, {
        [classes.active]: active,
      })}
    >
      {completed ? <Check className={classes.completed} /> : <div className={classes.circle} />}
    </div>
  );
}

QontoStepIcon.propTypes = {
  active: PropTypes.bool,  
  completed: PropTypes.bool,
};

const ColorlibConnector = withStyles({
  alternativeLabel: {
    top: 22,
  },
  active: {
    '& $line': {
      backgroundImage:
        'linear-gradient( 95deg,rgb(242,113,33) 0%,rgb(233,64,87) 50%,rgb(138,35,135) 100%)',
    },
  },
  completed: {
    '& $line': {
      backgroundImage:
        'linear-gradient( 95deg,rgb(242,113,33) 0%,rgb(233,64,87) 50%,rgb(138,35,135) 100%)',
    },
  },
  line: {
    height: 3,
    border: 0,
    backgroundColor: '#eaeaf0',
    borderRadius: 1,
  },
})(StepConnector);

const useColorlibStepIconStyles = makeStyles({
  root: {
    backgroundColor: '#ccc',
    zIndex: 1,
    color: '#fff',
    width: 50,
    height: 50,
    display: 'flex',
    borderRadius: '50%',
    justifyContent: 'center',
    alignItems: 'center',
  },
  active: {
    backgroundImage:
      'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
    boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)',
  },
  completed: {
    backgroundImage:
      'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
  },
});

function ColorlibStepIcon(props) {
  const classes = useColorlibStepIconStyles();
  const { active, completed } = props;

  const icons = {
    1: <PersonIcon />,    
    2: <EventIcon />,
    3: <CameraIcon />,
    4: <PlaylistAddCheckIcon />
  };
  return (
    <div
      className={clsx(classes.root, {
        [classes.active]: active,
        [classes.completed]: completed,
      })}
    >
      {icons[String(props.icon)]}
    </div>
  );
}

ColorlibStepIcon.propTypes = {
  /**
   * Whether this step is active.
   */
  active: PropTypes.bool,
  /**
   * Mark the step as completed. Is passed to child components.
   */
  completed: PropTypes.bool,
  /**
   * The label displayed in the step icon.
   */
  icon: PropTypes.node,
};

Formik & Yup

These are in the function for getSteps() and can be modified to add or remove steps along with getStepContent() that is placed inside the main function.

function getSteps() {
  return ['Personal Info', 'Additional Info', 'Add Images'];
}

Validation Schemas

Each form has it’s own validation schema for the group of fields that need validation

Personal Information Form

The personal information form will have three required fields (name, email, & phone) and one non-required field (location).

With masked phone number

const phoneRegExp = /^((\+[1-9]{1,4}[ -]?)|(\([0-9]{2,3}\)[ -]?)|([0-9]{2,4})[ -]?)*?[0-9]{3,4}[ -]?[0-9]{3,4}$/;

const personalInfoValidationSchema = yup.object({
  name: yup
    .string()
    .min(2, 'Name should be of minimum 2 characters length')
    .required('Name is required'),
  email: yup
    .string()
    .email('Enter a valid email')
    .required('Email is required'),    
  phone: yup
    .string()
    .matches(phoneRegExp, 'Phone number is not valid')
    .required('Phone is required'),  

  location: yup
    .string('Not Required')                  
  });

Event Details Form

The Event form has four fields. The two dates that have to be within 60 days from the current date. The end cannot be a date before the start date. The Event Title and Event Description fields are also required with a minimum and maximum characters rule.

  const eventValidationSchema = yup.object({
    title: yup
      .string()
      .min(3, 'Title should be of minimum 3 characters')
      .required('Title is required'),
    description: yup
      .string()
      .min(4, 'Description should be of minimum 4 characters')
      .required('Description is required'),  
    startDate: yup
      .date()
      .min(addDays(new Date(), 1))
      .max(addDays(new Date(), 59))      
      .required('Start Date is required'),  
    endDate: yup
      .date()
      .min(
        yup.ref('startDate'),
        "End date can't be before start date"
      )    
      .max(addDays(new Date(), 60))      
      .required('End Date is required'),            
  });

Image Upload Form

This form will have a “Yes” or “No” radio button that is required. The dropzone will be required to have at least one file if the answer is “Yes”. As of the time of this article, I was unable to figure out how to validate the files array. So the dropzone validation is handled with a “useState()” hook for a field labeled “files” and checks the length of the file array to be greater than the initial value of 0. Using Yup, create a field labeled filesCount and set a condition to have it required when the hasImagesToUpload has a value of “Yes”. If the value is “Yes” then use the test() method to check if the files array length is greater than 0.

const imageUploadValidationSchema = yup.object().shape({
    hasImagesToUpload: yup
      .string()
      .required('Do you have any images'),
    filesCount: yup    
    .number()    
    .when("hasImagesToUpload", {
      is: "Yes",
      then: yup.number().required('Images are required if you answered Yes above').test(val => val < files.length)
    })      
});

State Variable

Create a state variable activeStep to keep track of the current step/form.

const [activeStep, setActiveStep] = useState(0);
const steps = getSteps();

Terms & Condition Form

The last form Terms & Conditions will check if the user click the checkbox. If not a Material alert section will show up above the checkbox field.

  const termConditionsSchema = yup.object().shape({
    isTermChecked: yup      
      .boolean("Agree to Terms & Conditions")
      .required("Please Agree to Terms & Conditions")
      .test(val => val !== false)
  });

Formik Forms with useFormik Hook

The useFormik() hook validates each form against the Yup validation schemas.

Below are all 4 forms.

const formikPersonalInfo = useFormik({
    initialValues: {
      name: '',        
      email: '',
      phone: '',
      location: '',
    },
    validationSchema: personalInfoValidationSchema,    
    onSubmit: values => {     
      handleNext();  
    },
  });

  const formikEvents = useFormik({
    initialValues: {
      title: '',
      description: '',
      startDate: format(addDays(new Date(), 1), 'yyyy-MM-dd'),
      endDate: format(addDays(new Date(), 31), 'yyyy-MM-dd')      
    },    
    validationSchema: eventValidationSchema,
    onSubmit: values => {
      handleNext();  
    },
  });
  
  const formikImageUpload = useFormik({    
    initialValues: {
      hasImagesToUpload: '',
      filesCount: 0
    },    
    validationSchema: imageUploadValidationSchema,
    onSubmit: values => {      
      handleNext();  
    },
  });

  const formikTermsConditions = useFormik({    
    initialValues: {
      isTermChecked: false,      
    },    
    validationSchema: termConditionsSchema,
    onSubmit: values => {                  
      handleFormSubmit();  
    },
  });

Handle Submit

The handleSubmit() function executes the formik validations by the current step.

  const handleSubmit = () => {        
    switch (activeStep) {
      case 0: formikPersonalInfo.handleSubmit(); break;
      case 1: formikEvents.handleSubmit(); break;      
      case 2: formikImageUpload.handleSubmit(); break;      
      case 3: formikTermsConditions.handleSubmit(); break;      
      
    }                        
  };
  

Set and Render Step

Traversing the step counter and the content rendered with handleNext(), handleBack(), and getStepCount(step).

const handleNext = () => {    
   setActiveStep((prevActiveStep) => prevActiveStep + 1);
};

const handleBack = () => {
  setActiveStep((prevActiveStep) => prevActiveStep - 1);
};

const handleReset = () => {
   setActiveStep(0);
};

const getStepContent = (step) => {
    switch (step) {
        case 0:
        return (<>
                    <PersonalInfoForm  formik={formikPersonalInfo} />
                </>);
        case 1:
          return (<>
                    <EventForm formik={formikEvents} />
                </>);
        case 2:        
          return (<>
                    <AddImageForm handleDropzoneChange={handleDropzoneChange} 
                                  formik={formikImageUpload}
                                  files={files}
                                  handleDropzoneChange={handleDropzoneChange}
                                  handleIsFileChange={handleIsFileChange} 
                              />
                </>);
        case 3:        
          return (<>
                  <TermsConditionsForm  formik={formikTermsConditions}
 />
                 </>);                
        default:
        return 'Unknown step';
   }
}

Step Titles

These are in the function for getSteps() and can be modified to add or remove steps along with getStepContent() that is placed inside the main function.

function getSteps() {
  return ['Personal Info', 'Additional Info', 'Add Images'];
}

Handling Form Submit

handleFormSubmit() is called in the final step and gathers all the inputs to be displayed in JSON data in a JavaScript alert(). Animated Gif below.

  const handleFormSubmit = () => {
    var data = {
      Name: formikPersonalInfo.values.name,        
      Email: formikPersonalInfo.values.email,        
      Phone: formikPersonalInfo.values.phone,        
      Location: formikPersonalInfo.values.location,        
      EventTitle: formikEvents.values.title,        
      EventDescription: formikEvents.values.description,              
      StartDate: formikEvents.values.startDate,        
      EndDate: formikEvents.values.endDate,        
      Files: files      
    }
    console.log('handleFormSubmit ', data);
    alert(JSON.stringify(data, null, 2));
 }

The Rest of the Parent Form

The rest of the parent form has functions to handle the files array, has files for validation, and Terms & Agreements checkbox.


  const handleDropzoneChange = (files) => {       
    setFiles(files);       
  }

  const handleIsFileChange = (val) => {    
    setHasFiles(val);    
  }

  const handleIsTermChecked = (val) => {    
    setIsTermChecked(val);    
  }

  return (
    <React.Fragment>
        <Grid container spacing={3}>
        <Grid item xs={12} lg={3} xl={3}></Grid>
        <Grid item xs={12} lg={6} xl={6}>
        <Paper className={classes.paper}>
          <form noValidate autoComplete="off">
            <Stepper alternativeLabel activeStep={activeStep} connector={<ColorlibConnector />}>
              {steps.map((label) => (
                <Step key={label}>
                  <StepLabel StepIconComponent={ColorlibStepIcon}>{label}</StepLabel>
                </Step>
              ))}
            </Stepper>
            <div>
              {activeStep === steps.length ? (
                <div>
                  <Typography className={classes.instructions}>
                    All steps completed - you&apos;re finished
                  </Typography>
                  <Button onClick={handleReset} className={classes.button}>
                    Reset
                  </Button>
                </div>
              ) : (
                <div>
                  <Typography className={classes.instructions}>{getStepContent(activeStep)}</Typography>
                  
                  <React.Fragment>
                    <div className={classes.buttons}>
                    <Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
                      Back
                    </Button>
                    {activeStep === steps.length - 1 ?
                      <Button
                            variant="contained"
                            // disabled={!isStepValidated(activeStep) }
                            color="primary"
                            onClick={handleSubmit}
                            className={classes.button}
                        >
                          Finish
                      </Button>
                      :
                      <Button
                          variant="contained"                          
                          color="primary"                          
                          onClick={handleSubmit}
                          className={classes.button}
                      >
                        Next
                     </Button>
                    }
            
                  </div>
                  </React.Fragment>
                </div>
              )}
            </div>
          </form>
          </Paper>
        </Grid>
        <Grid item xs={12} lg={3} xl={3}></Grid>                
      </Grid>          
    </React.Fragment>
  );
}

The Step Forms

There are four individual forms in this example.

  • PersonalInfoForm.js
  • EventForm.js
  • AddImagesForm.js
  • TermsConditionsForm.js

Personal Information Form

This form displays the 4 fields for name, email, phone, and location. All of the fields except for location will be validated before a user can move on to the next form.

Each input uses formik to handle onChange(), onBlur() to check if if the input is pristine, error to highlight the input in red, helperText for the error message as shown below.

onChange={props.formik.handleChange}
onBlur={props.formik.handleBlur}
value={props.formik.values.name}
error={props.formik.touched.name && Boolean(props.formik.errors.name)}
helperText={props.formik.touched.name && props.formik.errors.name}         

Code for the entire Personal Information Form

import React  from 'react'
import PropTypes from 'prop-types';
import MaskedInput from 'react-text-mask';
import Grid from '@material-ui/core/Grid';
import Container from '@material-ui/core/Container';
import TextField from '@material-ui/core/TextField';
import InputLabel from '@material-ui/core/InputLabel';
import FormControl from '@material-ui/core/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText';
import OutlinedInput from '@material-ui/core/OutlinedInput';
function TextMaskCustom(props) {
    const { inputRef, ...other } = props;
  
    return (
      <MaskedInput      
        {...other}
        ref={(ref) => {
          inputRef(ref ? ref.inputElement : null);
        }}
        mask={['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/]}
        placeholderChar={'\u2000'}
        showMask
      />
    );
}

TextMaskCustom.propTypes = {
    inputRef: PropTypes.func.isRequired,
};

export default function PersonalInfoForm(props) {

    return (
        <>
            <Container maxWidth="sm">
            <Grid container spacing={3}>
                <Grid item xs={6} >
                    <TextField
                        fullWidth
                        variant="outlined"
                        size="small"
                        label="Name"
                        id="name"
                        name="name"
                        type="text"                                                
                        onChange={props.formik.handleChange}
                        onBlur={props.formik.handleBlur}
                        value={props.formik.values.name}
                        error={props.formik.touched.name && Boolean(props.formik.errors.name)}
                        helperText={props.formik.touched.name && props.formik.errors.name}         
                        InputLabelProps={{
                            shrink: true,
                        }}
                    />                        
                </Grid>
                <Grid item xs={1} ></Grid>
                <Grid item xs={5} >                    
                    <TextField
                        fullWidth
                        variant="outlined"
                        size="small"
                        label="Location"
                        id="location"
                        name="location"
                        type="text"                        
                        
                        onChange={props.formik.handleChange}
                        onBlur={props.formik.handleBlur}
                        value={props.formik.values.location}                            
                        helperText="(Optional)"
                        InputLabelProps={{
                            shrink: true,
                        }}

                    />                        
                </Grid>

                <Grid item xs={6} >                    
                    <TextField
                        fullWidth
                        size="small"
                        variant="outlined"
                        label="Email"
                        id="email"
                        name="email"
                        type="email"
                        onChange={props.formik.handleChange}
                        onBlur={props.formik.handleBlur}
                        value={props.formik.values.email}
                        error={props.formik.touched.email && Boolean(props.formik.errors.email)}
                        helperText={props.formik.touched.email && props.formik.errors.email}
                        InputLabelProps={{
                            shrink: true,
                        }}

                    />                    
                </Grid>
                <Grid item xs={1} ></Grid>
                <Grid item xs={5} >
                    
                    <FormControl  variant="outlined" size={'small'} fullWidth>
                            <InputLabel htmlFor="phone">Phone</InputLabel>        
                                <OutlinedInput                                     
                                    
                                    value={props.formik.values.phone}
                                    error={props.formik.touched.phone && Boolean(props.formik.errors.phone)}
                                    helperText={props.formik.touched.phone && props.formik.errors.phone}    
                                    onChange={props.formik.handleChange}
                                    
                                    name="phone"
                                    id="phone"
                                    label="Phone"
                                    size={'small'}          
                                    style={{marginTop: -1}}
                                    InputLabelProps={{
                                        shrink: true,
                                    }}
                                    inputComponent={TextMaskCustom}
                                />
                            <FormHelperText id="my-helper-text" error={props.formik.touched.phone && Boolean(props.formik.errors.phone)}>
                            {props.formik.errors.phone}
                            </FormHelperText>
                        </FormControl>                                            
                    
                </Grid>
        </Grid>
        </Container>            
        </>
    )
}

Event Form

The Event Form has a title, description, and two dates fields that are required.

import React from 'react';
import Grid from '@material-ui/core/Grid';
import Container from '@material-ui/core/Container';
import TextField from '@material-ui/core/TextField';
import styles from 'styles/EventForm.module.css';

export default function EventForm(props) {   

    return (
        < >
            <Container maxWidth="sm">
            <Grid container spacing={3}>
                <Grid item xs={12} >
                    <TextField placeholder=""                                     
                                    label="Event Title"      
                                    fullWidth={true}                                                                                        
                                    variant="outlined"
                                    size="small"           
                                    type="text"     
                                    name="title"
                                    className={styles.textField}
                                    onChange={props.formik.handleChange}
                                    onBlur={props.formik.handleBlur}
                                    value={props.formik.values.title}
                                    error={props.formik.touched.title && Boolean(props.formik.errors.title)}
                                    helperText={props.formik.touched.title && props.formik.errors.title}         
            
                                />
                </Grid>

                <Grid item xs={12} >

                    <TextField label="Event Details"                                         
                                        variant="outlined"
                                        fullWidth={true}
                                        size="small"
                                        type="text"    
                                        name="description"
                                        multiline
                                        minRows={4}
                                        className={styles.textField}
                                        onChange={props.formik.handleChange}
                                        onBlur={props.formik.handleBlur}
                                        value={props.formik.values.description}
                                        error={props.formik.touched.description && Boolean(props.formik.errors.description)}
                                        helperText={props.formik.touched.description && props.formik.errors.description}             
                                 />
                    
                </Grid>

                <Grid sm={5}>                        
                   
                    <TextField label="Start Date"
                                variant="outlined"
                                size="small"
                                name="startDate"
                                id="startDate"                                
                                type="date"
                                style={{marginTop: '17px', marginLeft: '12px'}}
                                defaultValue={props.formik.values.startDate}                        
                                onChange={props.formik.handleChange}
                                onBlur={props.formik.handleBlur}
                                value={props.formik.values.startDate}
                                error={props.formik.touched.startDate && Boolean(props.formik.errors.startDate)}
                                helperText={props.formik.touched.startDate && props.formik.errors.startDate}                                   
                                
                                InputLabelProps={{
                                    shrink: true,
                                }}
                    />
                
                </Grid>
                <Grid sm={2}></Grid>                        
                <Grid sm={5} align="right">
                    <TextField label="End Date"
                                variant="outlined"
                                size="small"
                                name="endDate"
                                id="endDate"                                
                                type="date"
                                style={{marginTop: '17px', marginRight: '12px'}}
                                defaultValue={props.formik.values.endDate}                        
                                onChange={props.formik.handleChange}
                                onBlur={props.formik.handleBlur}
                                value={props.formik.values.endDate}
                                error={props.formik.touched.endDate && Boolean(props.formik.errors.endDate)}
                                helperText={props.formik.touched.endDate && props.formik.errors.endDate}   
                                
                                InputLabelProps={{
                                    shrink: true,
                                }}
                    />      

                </Grid>
            </Grid>
        </Container>
            
        </>
    )
}

Add Images Form

import React from 'react';
import { DropzoneArea } from 'material-ui-dropzone';
import FormControl from '@material-ui/core/FormControl';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormLabel from '@material-ui/core/FormLabel';
import AttachFile from '@material-ui/icons/PhotoCamera';
import styles from 'styles/UploadFileForm.module.css';
import Container from '@material-ui/core/Container';
import Alert from '@material-ui/lab/Alert';

export default function AddImageForm(props) {
        
    return (
        <React.Fragment>
            <Container maxWidth="sm">
            <FormControl component="fieldset" className={styles.formControl}>
               {(props.formik.touched.hasImagesToUpload && Boolean(props.formik.errors.hasImagesToUpload)) ?               
                <>
                    <FormLabel component="legend" style={{color: 'red'}}>Do you have any images? (Required)</FormLabel>                                        
                </>               
               :
                <>
                    <FormLabel component="legend" color="error">Do you have any images?</FormLabel>                    
                </>
               }

                <RadioGroup row aria-label="position" 
                            name="hasImagesToUpload"
                            onChange={props.formik.handleChange}
                            onBlur={props.formik.handleBlur}
                            value={props.formik.values.hasImagesToUpload}
                            error={props.formik.touched.hasImagesToUpload && Boolean(props.formik.errors.hasImagesToUpload)}
                            helperText={props.formik.touched.hasImagesToUpload && props.formik.errors.hasImagesToUpload}         
                            >
                    <FormControlLabel
                        value="Yes"
                        control={<Radio color="primary" />}
                        label="Yes"
                        labelPlacement="start"                        
                    />
                    <FormControlLabel
                        value="No"
                        control={<Radio color="primary" />}
                        label="No"
                        labelPlacement="start"
                    />                    
                </RadioGroup>
                
            </FormControl>       
            {(props.formik.touched.filesCount && Boolean(props.formik.errors.filesCount) && props.files.length === 0)?
            <Alert severity="error">Please upload 1 to 3 images</Alert>
            :
            ''
            }
             {props.formik.values.hasImagesToUpload == "Yes"?
                <DropzoneArea   filesLimit={3} 
                                //onChange={props.formik.handleChange}
                                previewText="Selected files"
                                // useChipsForPreview
                                onChange={props.handleDropzoneChange }                                
                                // values={props.files}
                                initialFiles={props.files}
                                // initialFiles={props.formik.values.files}
                                Icon={AttachFile}                                
                                acceptedFiles={['image/*']}
                                showAlerts={['error']}                                 
                                dropzoneText="Click here to upload photo"
                                dropzoneClass={styles.dropZoneCls} />
                : ''}
            </Container>
        </React.Fragment>
    )
}

Terms & Conditions – Last Form

Finally, the last form which requires a checkbox to be clicked before a submit. The submit will show the input as JSON data in an alert() popup.

import React from 'react';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Checkbox from '@material-ui/core/Checkbox';
import Alert from '@material-ui/lab/Alert';
export default function TermsConditionsForm(props) {
    
    return (
        <div >
            
            <Box component="div" m={8} align="left">
                Lorem Ipsum is simply dummy text of the printing and typesetting industry. 
                Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, 
                when an unknown printer took a galley of type and scrambled it to make a type specimen book. 
                It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. 
                It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, 
                and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsu
            </Box>          
            {(props.formik.touched.isTermChecked && Boolean(props.formik.errors.isTermChecked))?
                <Alert severity="error">Please agree to the Terms & Conditions </Alert>
            :
                ''
            }
            <Typography variant="h6" align="center">
                Terms & Conditions   
                 <Checkbox 
                          name="isTermChecked"
                          checked={props.formik.values.isTermChecked}
                          onChange={props.formik.handleChange}
                          onBlur={props.formik.handleBlur}                                    
                          inputProps={{ 'aria-label': 'primary checkbox' }} />  
            </Typography>
        </div>
    )
}

That’s it.

Please get the source files from

Quick overview of the app.