Type to generate UI components from text

OR

Browse thousands of MUI, Tailwind, React components that are fully customizable and responsive.

Explore Components

Creating Beautiful Forms in React with Material UI Form

Forms are an integral part of user interaction in web applications. And, when it comes to building modern, responsive, and aesthetically pleasing forms in React, Material UI form components are a popular choice.

In this article, we will explore the process of creating forms in React using Material UI form components. We’ll dive into the world of Material UI, leveraging its pre-built components to design forms that not only function seamlessly but also look visually appealing. Whether you’re a seasoned React developer or just getting started, this guide will provide you with the knowledge and tools to craft user-friendly forms that enhance the user experience of your web applications.

Does Material UI have a Form Component?

Material UI doesn’t have an actual form component per se. So, it’s expected that the React JSX form component that is `<form></form>` will be used to wrap all the Material UI form-related components in order to submit the form data.

Prerequisites

To follow along with this article, you need to understand the basics of React, CSS, JavaScript, and Material UI.

Getting Started With Material UI Library

Material UI is an exquisite open-source React component library. The creators thoughtfully crafted it with Google’s Material Design principles, ensuring both stunning aesthetics and functionality.

Material UI offers lots of components, but the scope of this article covers all the prebuilt Material U input components. With that let’s create our React application, and start creating forms.

PS: Engineers waste time building and styling components when working on a project, writing repetitive markup adds time to the project and is a mundane, boring task for engineers. PureCode.ai uses AI to generate a large selection of custom, styled UI components for different projects and component types.

Creating a React App

We’ll be using Vite to bootstrap the new React app. To start, run the command below:

npm create vite@latest

The command above asks for some prompt, which can be seen in the image below:

Vite init React.js app

Once the installation is complete, navigate to the project folder and install the dependencies.

Installing Material UI

To install material UI, run the command below:

npm install @mui/material @emotion/react @emotion/styled

Once all the dependencies are installed, we can get to the code.

Material UI Form Example

Now that we have Material UI setup on our codebase, by the end of this article, we will have built a simple checkout page using Material UI and React hook form for managing the form data.

Checkout Form

Please note there will be code snippets for each form input component mentioned in the article, I strongly advise you to create a component folder and try the examples for yourself.

Material UI Form Components

In Material UI, the form component can also be called the inputs component is made of the TextField, Checkbox, Button, Radio components and so many others which are in the image below. In this article, we’ll be going over 7 of the form components.

MUI Form Components

TextField

The Textfield component allows users to enter texts in the UI, which is one of the common things required in a form.

The text field component comes with lots of props that help extend the looks and functionality of the component. Also, most of the standard form HTML input attributes, which are props in react are supported e.g. `disabled`, `required` etc.

Let’s look at some examples of how the props can change the look and functionality of the Textfield component:

Textfield VariantsThe text field component has three variants: outlined (default), filled, and standard, as of v5.14.14.

TextField Variants Visualization

Here is the code for the illustration above:

import { TextField } from '@mui/material';
import React from 'react';

function TextFieldVariants() {
  return (
    <div>
      <h2>TextField Variants</h2>

      <TextField variant='outlined' placeholder='Outlined' />
      <TextField variant='filled' placeholder='Filled' />
      <TextField variant='standard' placeholder='Standard' />
    </div>
  );
}

export default TextFieldVariants;

Place the TextFieldVariants component into the component folder or area of choice to see the preview.

Textfield Type – Date InputWith the type prop and date as the value on the Text field component, you’ll get a date field. Notice the variant prop still applies across the Text field.

MUI TextField Date Input

Here is the code for the illustration above:

import { TextField } from '@mui/material';
import React from 'react';

function TextFieldDate() {
  return (
    <div>
      <h2>TextField Date Input</h2>

      <TextField type='date' variant='outlined' placeholder='Outlined' />
      <TextField type='date' variant='filled' placeholder='Filled' />
      <TextField type='date' variant='standard' placeholder='Standard' />
    </div>
  );
}

export default TextFieldDate;

To learn more about Textfield API and props, visit the TextField documentation.

Now that we’ve gone over the Text field component which is mostly used when it comes to forms, let’s see two other components that are used with Material UI inputs, which are the form control component and the form group component.

Form Control

The FormControl component is used to provide context such as filled/focused/error/required state for form inputs. Basically, it wraps around other form inputs like the text fields component making sure the state always stays consistent.

Here’s an example with code:

import {
  TextField,
  FormControl,
  InputLabel,
  Input,
  FormHelperText,
  Box,
} from '@mui/material';
import React, { useState } from 'react';

function FormControlComp() {
  const [name, setName] = useState('');
  const [isError, setIsError] = useState(false);

  const handleName = (e) => {
    setName(e.target.value);
    if (name.length > 6) {
      setIsError(true);
      alert('error: allows 6 character for your name ');
    } else {
      setIsError(false);
    }
  };

  return (
    <div>
      <h2>Form Control</h2>

      <Box sx={{ display: 'flex', gap: 5 }}>
        <FormControl error={isError}>
          <TextField
            variant='outlined'
            placeholder='Name'
            onChange={handleName}
            error={isError}
          />
        </FormControl>

        <FormControl variant='standard'>
          <InputLabel>Name</InputLabel>
          <Input  />
        </FormControl>

        <FormControl error variant='standard'>
          <InputLabel htmlFor='component-error'>Name</InputLabel>
          <Input id='component-error' aria-describedby='component-error-text' />
          <FormHelperText id='component-error-text'>Error</FormHelperText>
        </FormControl>
      </Box>
    </div>
  );
}

export default FormControlComp;

From the code snippet above, we’ve introduced three new components, which are InputLabel, Input and FormHelperText component. These components make up the TextField component, so the Textfield is composed of all the former components mentioned.

The InputLabel is to display the label for the input, while the FormHelperText displays additional information, like the state of the input.

Notice how the last component in the image is in the error state because, on the FormConrtol component, the prop error was added.

 <FormControl error variant='standard'>
     <InputLabel htmlFor='component-error'>Name</InputLabel>
     <Input id='component-error' aria-describedby='component-error-text' />
     <FormHelperText id='component-error-text'>Error</FormHelperText>
  </FormControl>

Form Group

The FormGroup component wraps around the Checkbox and Switch components, providing a compact row layout.

The Props for the FormGroup component are children i.e. (Checkbox and Switch), classes, row, and sx (a prop to override CSS styles).

MUI Form Group

An example of the FormGroup component will be shown in the Checkbox section.

Checkbox

The Checkbox component allows the user to turn an option on or off, toggling between the two states. Here’s an example code:

import * as React from 'react';
import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';

export default function Checkboxes() {
  return (
    <>
      <h2>Checkbox</h2>

      <FormGroup>
        <FormControlLabel control={<Checkbox defaultChecked />} label='Label' />
        <FormControlLabel required control={<Checkbox />} label='Required' />
        <FormControlLabel disabled control={<Checkbox />} label='Disabled' />
      </FormGroup>
    </>
  );
}

Here’s the preview:

MUI Checkbox

Noticed the FormControlLabel component is used in the code snippet above. The FormControllabel component is used to add a label to the checkbox, which is needed to tell what the checkbox signifies. One other feature of the component is that once the label is clicked, the checkbox will toggle between the state on or off.

To learn more about the FormControllabel, Checkbox and Switch components click on the links.

Radio Group

The Radio Group component is more like the Form Group component, but it works for the Radio Component which is for selecting one option out of other options.

import * as React from 'react';
import Radio from '@mui/material/Radio';
import RadioGroup from '@mui/material/RadioGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormControl from '@mui/material/FormControl';
import FormLabel from '@mui/material/FormLabel';

export default function RadioButtonsGroup() {
  return (
    <FormControl>
      <FormLabel id='demo-radio-buttons-group-label'>Gender</FormLabel>
      <RadioGroup defaultValue='female' name='radio-buttons-group'>
        <FormControlLabel value='female' control={<Radio />} label='Female' />
        <FormControlLabel value='male' control={<Radio />} label='Male' />
        <FormControlLabel value='other' control={<Radio />} label='Other' />
      </RadioGroup>
    </FormControl>
  );
}

Here’s the preview:

MUI Radio Group

From the snippet above, notice the FormControl component is used to wrap around the RadioGroup, managing its state. Also, the FormLabel component is used to show the label that identifies the RadioGroup.

To learn more about the RadioGroup component, visit the documentation here.

Select

The select component is like the traditional select element we have in HTML, but with Material UI fill. Instead of the option tag in HTML, the Select component makes use of the MenuItem component that has the prop value to select a value.

Here’s how the select component can be used:

import * as React from 'react';
import Box from '@mui/material/Box';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';

export default function SelectComp() {
  const [color, setColor] = React.useState('');

  const handleChange = (event) => {
    setColor(event.target.value);
  };

  return (
    <Box sx={{ minWidth: 280 }}>
      <FormControl variant='filled' fullWidth>
        <InputLabel id='demo-simple-select-label'>
          What's your favourite colour?
        </InputLabel>
        <Select
          labelId='demo-simple-select-label'
          id='demo-simple-select'
          value={color}
          label='Favourite Colour?'
          onChange={handleChange}
        >
          <MenuItem value={'red'}>Red</MenuItem>
          <MenuItem value={'yellow'}>Yellow</MenuItem>
          <MenuItem value={'black'}>Black</MenuItem>
        </Select>
      </FormControl>
    </Box>
  );
}

Here’s the preview of the code:

Select component in MUI

To learn more about the Select component, visit the documentation here.

AutoComplete

The AutoComplete component is a more advanced Select component, that allows you to search for a value, and then the options change to meet the value being searched for. In essence like the name of the component, the complete helps to auto-complete the value being typed into the component. To illustrate this, let’s see the code below:

import * as React from 'react';
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';

export default function AutoCompleteComp() {
  return (
    <>
      <h2>AutoComplete</h2>

      <Autocomplete
        disablePortal
        id='combo-box-demo'
        options={top8Films}
        sx={{ width: 300 }}
        renderInput={(params) => <TextField {...params} label='Movie' />}
      />
    </>
  );
}

// 8 films as rated by IMDb users. http://www.imdb.com/chart/top
const top8Films = [
  { label: 'The Shawshank Redemption', year: 1994 },
  { label: 'The Godfather', year: 1972 },
  { label: 'The Godfather: Part II', year: 1974 },
  { label: 'The Dark Knight', year: 2008 },
  { label: '12 Angry Men', year: 1957 },
  { label: "Schindler's List", year: 1993 },
  { label: 'Pulp Fiction', year: 1994 },
  {
    label: 'The Lord of the Rings: The Return of the King',
    year: 2003,
  },
];

Here’s the preview of the code. Notice the value searched for is the and all the movies that have the word the in their label shows.

Autocomplete in MUI

There are more advance use cases for the AutoComplete component, you can learn more about it here.

Building a Custom TextInput Handler Function

Building a custom TextInput handler function with Material UI form involves creating a custom component that handles the input value and updates it when the user types or selects a value. Here is an example of how you can achieve this:

import { TextField } from '@mui/material';
import React from 'react';

const CustomTextInput = ({ label, value, onChange }) => {
  const handleChange = (event) => {
    onChange(event.target.value);
  };

  return (
    <TextField
      variant='filled'
      label={label}
      value={value}
      onChange={handleChange}
    />
  );
};

export default CustomTextInput;

In this example, we create a custom component called `CustomTextInput` that takes `value`, `label` and `onChange` as props. The `value` prop represents the current value of the input, the `label` prop shows the label for the input and the `onChange` prop is a callback function that will be called whenever the input value changes.

Inside the component, we define a `handleChange` function that updates the input value using the `onChange` prop callback. This function is called whenever the user types or selects a value in the input field.

Finally, we render a Material UI `TextField` component with the provided `value` and `onChange` props. You can also pass additional props to the `TextField` component as needed.

Here’s how we utilise the custom input component created

import React, { useState } from 'react';
import CustomTextInput from './CustomInput';

function UseCustomTextInput() {
  const [name, setName] = useState('');

  const onChange = (value) => {
    setName(value);
  };

  return (
    <div>
      <h2>Using Custom Text Input</h2>

      <CustomTextInput value={name} onChange={onChange} label='Name' />
    </div>
  );
}

export default UseCustomTextInput;

Here’s the preview of the code:

Custom Text Input in MUI

This use case for the custom text input used here is very simple, but very important especially when it you have a UI style in your component you’d like to reuse.

Validating Material UI Forms

When it comes to form validation in Material UI there are a lot of approaches to handle it. Using external libraries like react-hook-form and formik can be very beneficial, cause they’ll handle all the states like error, disabled, and focus for you.

In this article, we wouldn’t be using external libraries, but we will be handling the validation ourselves, using plain React state. Let’s look at an example showing how to handle validation error state in Material UI input components.

import React, { useState } from 'react';
import { TextField, Button, FormControl, Box } from '@mui/material';

const ValidatingForm = () => {
  const [formValues, setFormValues] = useState({
    name: '',
    email: '',
  });

  const [formErrors, setFormErrors] = useState({
    name: '',
    email: '',
  });

  const validateForm = () => {
    let valid = true;
    const newErrors = {
      name: '',
      email: '',
    };

    if (!formValues.name) {
      newErrors.name = 'Name is required';
      valid = false;
    }

    if (!formValues.email) {
      newErrors.email = 'Email is required';
      valid = false;
    } else if (!/S+@S+.S+/.test(formValues.email)) {
      newErrors.email = 'Invalid email address';
      valid = false;
    }

    setFormErrors(newErrors);
    return valid;
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    if (validateForm()) {
      // Perform form submission or other actions
      alert(JSON.stringify(formValues));
      console.log('Form submitted successfully');
    }
  };

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormValues((prevValues) => ({
      ...prevValues,
      [name]: value,
    }));
  };

  return (
    <>
      <h2>Form Validation</h2>

      <form onSubmit={handleSubmit}>
        <TextField
          label='Name'
          name='name'
          value={formValues.name}
          onChange={handleChange}
          error={!!formErrors.name}
          helperText={formErrors.name}
          fullWidth
          sx={{ mb: 3 }}
        />

        <TextField
          label='Email'
          name='email'
          value={formValues.email}
          onChange={handleChange}
          error={!!formErrors.email}
          helperText={formErrors.email}
          fullWidth
          sx={{ mb: 3 }}
        />
        <Button type='submit' variant='contained' fullWidth color='primary'>
          Submit
        </Button>
      </form>
    </>
  );
};

export default ValidatingForm;

In the above code, we use the useState hook to manage form state and errors. The handleSubmit function is called when the form is submitted. Inside this function, we perform validation by checking if the required fields (name and email) are filled in the `validateForm` function. If any validation errors occur, we update the `formErrors` state object. The error and helperText props of the TextField component are used to display the validation errors.

form validation

Building a Submit Handler Function for Material UI Form

From our previous code snippet, we’ve handled the submission of form data already. But to expatiate on submitting the form data, the `onSubmit` event handler is used on the form JSX component. An example of this is below:

<form onSubmit={handleSubmit}>
   <TextField label="Your message" />
</form>

The handleSubmit function takes the event argument. Then we’ll need to prevent the default browser behaviour of refreshing a page after a form is submitted by using the `preventDefault()` method.

const handleSubmit = (event) => {
  event.preventDefault();
  alert("Form Submitted");
}

Putting It All Together

To sum it all up, we’ve discussed how forms are created with the Material UI form component, and checking validation, so now let’s take all we’ve learned and build the checkout page.

Creating a Checkout Form Using Material UI Form

Now as promised, let’s put all we’ve gone through to build this checkout page:

Checkout page

Before we start with the checkout page, we’ll need to install react-hook-form first. To do this run the code in the terminal:

npm install react-hook-form

Once you’ve successfully installed react-hook-form, create a folder in the components folder as checkout, and then create the file, `Checkout.jsx`.

In the `Checkout.jsx` file, copy and paste the snippet below into the file, before we go over the implementation.

import * as React from 'react';
import { useForm } from 'react-hook-form';
import {
  AppBar,
  Container,
  Toolbar,
  Paper,
  TextField,
  Grid,
  Typography,
  Link,
  Button,
  Box,
  Select,
  MenuItem,
  FormGroup,
  FormControl,
  FormControlLabel,
  Checkbox,
  FormLabel,
  RadioGroup,
  Radio,
} from '@mui/material';

export default function Checkout() {
  const { handleSubmit, register } = useForm();

  const onSubmit = (value) => {
    alert(JSON.stringify(value));
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <React.Fragment>
        <Container component='main' maxWidth='sm' sx={{ mb: 4 }}>
          <Paper
            variant='outlined'
            sx={{ my: { xs: 3, md: 6 }, p: { xs: 2, md: 3 } }}
          >
            <Typography component='h1' variant='h4' align='center'>
              Checkout
            </Typography>

            <Box sx={{ my: 3 }}>
              <Typography variant='h6' gutterBottom>
                Shipping address
              </Typography>
              <Grid container spacing={3}>
                <Grid item xs={12} sm={6}>
                  <TextField
                    required
                    label='First name'
                    fullWidth
                    variant='standard'
                    {...register('firstName')}
                  />
                </Grid>
                <Grid item xs={12} sm={6}>
                  <TextField
                    required
                    label='Last name'
                    fullWidth
                    variant='standard'
                    {...register('lastName')}
                  />
                </Grid>
                <Grid item xs={12}>
                  <TextField
                    required
                    label='Address line'
                    fullWidth
                    variant='standard'
                    {...register('address')}
                  />
                </Grid>
                <Grid item xs={12}>
                  <FormLabel sx={{ textAlign: 'left' }}>Country</FormLabel>
                  <Select
                    required
                    label='Country'
                    fullWidth
                    variant='standard'
                    {...register('country')}
                  >
                    <MenuItem value='USA'>USA</MenuItem>
                    <MenuItem value='America'>America</MenuItem>
                    <MenuItem value='Nigeria'>Nigeria</MenuItem>
                  </Select>
                </Grid>
              </Grid>
            </Box>

            <Box sx={{ my: 3 }}>
              <Typography variant='h6' gutterBottom>
                Payment method
              </Typography>
              <Grid container spacing={3}>
                <Grid item xs={12} md={6}>
                  <TextField
                    required
                    label='Name on card'
                    fullWidth
                    variant='standard'
                    {...register('cardName')}
                  />
                </Grid>
                <Grid item xs={12} md={6}>
                  <TextField
                    required
                    label='Card number'
                    fullWidth
                    variant='standard'
                    {...register('cardNumber')}
                  />
                </Grid>
                <Grid item xs={12} md={6}>
                  <TextField
                    required
                    label='Expiry date'
                    fullWidth
                    variant='standard'
                    {...register('expDate')}
                  />
                </Grid>
                <Grid item xs={12} md={6}>
                  <TextField
                    required
                    label='CVV'
                    helperText='Last three digits on signature strip'
                    fullWidth
                    variant='standard'
                    {...register('cvv')}
                  />
                </Grid>
              </Grid>
            </Box>

            <Box>
              <RadioGroup
                defaultValue='payCard'
                row
                {...register('paymentType')}
              >
                <FormControlLabel
                  value='payCard'
                  control={<Radio />}
                  label='Pay by Card'
                />
                <FormControlLabel
                  value='payTransfer'
                  control={<Radio />}
                  label='Pay by Transfer'
                />
              </RadioGroup>
              <FormGroup>
                <FormControlLabel
                  control={
                    <Checkbox defaultChecked {...register('saveForLater')} />
                  }
                  label='Save the information for later'
                />
              </FormGroup>
            </Box>

            <Button
              type='submit'
              variant='contained'
              sx={{ mt: 3, ml: 1 }}
              fullWidth
            >
              Purchase
            </Button>
          </Paper>
        </Container>
      </React.Fragment>
    </form>
  );
}

From the snippet above, we’ve gone through all the form input components already, but what is new is the react hook form, which helps us capture the state of the input fields, which can then be submitted to some database.

The ` {…register(‘name’)}` function, is called on each input field to allow react hook form to manage the state of the fields. Notice for validation since the fields are required, the required prop is used. For a simple use case like this, it’s fine, but for a more complicated use case where you’d like to display error messages, using react hook form really shines there.

You can view the source code in this GitHub repository and you can also read this guide to learn how to use React hook form with Material UI.

Final Thought on Material UI Form Components

We’ve come to the end of this article, and gone over most of the basics when it comes to dealing with forms in Material UI, including validation. Here are further resources to use to learn about Material UI forms.

For your reference, you can also check out the below YouTube video to learn more about the React Hook form with Material UI integration.

PureCode.ai can cater for your code development process. It will save you valuable time and effort on a project by providing customized, and ready-to-use components which will allow you to prioritize more important and thought-intensive tasks to speed up the development of your user interface.

Shadrach Abba

Shadrach Abba