The Ultimate Guide to Material-UI FormControl: 3 Examples

The MUI FormControl component is used as a high-level wrapper around subcomponents in a form. It’s primary purpose is state management.

We will create a form that has three FormControl components inside, each wrapping a row of components.

MUI FormControl Example
MUI FormControl Example

In many UIs that use forms, likely only one FormControl would be used and would wrap all the child components to manage their state together.

The FormControl has several props for controlling state. The remaining props are used for sizing and spacing.

Here’s a YouTube version of this post or watch below:

The Ultimate Guide to Material-UI F...
The Ultimate Guide to Material-UI FormControl: 3 Examples

MUI FormControl Layout

The FormControl can be used to manage layout, especially if display: "flex" is passed to the sx prop. I will show how to do this here just for demo purposes. However, the FormGroup component is actually built for layout and I recommend using it instead.

The code below creates the first MUI Select and MUI TextField seen in the intro screenshot. I ran into several interesting problems as I worked on this section.

<FormControl 
  margin="normal" 
  color="primary" 
  variant="filled" 
  sx={{display: 'flex', flexDirection: 'row', justifyContent: 'space-around'}}
>
  {/* <FormGroup row sx={{justifyContent: 'space-around' }}> */}
  <Box>
  <InputLabel sx={{left: 'auto'}} id="custom-select-label">Score</InputLabel>
  <Select
    sx={{ width: 300 }}
    label={"Score"}
    MenuProps={{
      PaperProps: { sx: { maxHeight: 200 } }
    }}
  >
    {scoreData.map((scoreValue) => {
      return <MenuItem value={scoreValue}>{scoreValue}</MenuItem>
    })}
  </Select>
  </Box>
  <TextField
    sx={{ width: 300 }}
    label="Name"
    variant="filled"
    placeholder="Name"
  />
    {/* </FormGroup> */}
</FormControl>

The first issue is that justifyContent: "space-around" doesn’t play well with InputLabel inside FormControl. The FormControl cleverly adds left: 0 to the InputLabel so that it will slip inside the first component (the Select in this tutorial).

However, space-around pushes the Select to the right.

MUI FormControl InputLabel with space-around layout bug
MUI FormControl InputLabel with space-around layout bug

To solve this, I wrapped a Box component around the Select and InputLabel. This meant the Box was pushed to the right instead. Next, I added left: "auto" to the InputLabel so that it would not be at left: 0 still.

The next issue I faced was controlling layout without using a FormGroup. I added a commented out FormGroup to show how much less code is required to control flex with a FormGroup compared to a FormControl.

//Required for FormControl but not FormGroup
sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-around' }}

However, the FormGroup does add an extra div to the DOM.

MUI FormGroup DOM
MUI FormGroup DOM

MUI FormControl Error

FormControl is at its best when it is managing state for children components. Here’s an example where the FormControl error prop is given a value of true.

<FormControl variant="filled" margin="normal" error sx={{ width: '100%', marginTop: 2 }}>
  <FormGroup row sx={{ justifyContent: 'space-around' }}>
    <Select
      sx={{ width: 300 }}
      label={"Score"}
      MenuProps={{
        PaperProps: { sx: { maxHeight: 200 } }
      }}
    >
      {scoreData.map((scoreValue) => {
        return <MenuItem value={scoreValue}>{scoreValue}</MenuItem>
      })}
    </Select>
    <FormGroup row={false} sx={{ minWidth: 300 }}>
      <FormLabel component="legend">Form Switch</FormLabel>

      <FormControlLabel
        control={
          <Switch
            name="switch1"
          />
        }
        label="Switch 1"
      />
      <FormControlLabel
        control={
          <Switch
            name="switch2"
          />
        }
        label="Switch 2"
      />
      <FormControlLabel
        control={
          <Switch
            name="switch3"
          />
        }
        label="Switch 3"
      />
      <FormHelperText>Switch Group</FormHelperText>
    </FormGroup>
  </FormGroup>
</FormControl>

The error state affects the color style of the subcomponents but it does not affect their functionality. The Select and Switches are still usable.

MUI FormControl Error State Example
MUI FormControl Error State Example

MUI FormControl Disabled

In the code below I use the disabled prop to disable the children components of a FormControl. However, an interesting problem occurred when one of the child components was a TextField.

<FormControl variant="filled" margin="normal" disabled={disabled} fullWidth>
  <FormGroup row sx={{ justifyContent: 'space-around' }}>
    <Select
      sx={{ width: 300 }}
      label={"Score"}
      MenuProps={{
        PaperProps: { sx: { maxHeight: 200 } }
      }}
    >
      {scoreData.map((scoreValue) => {
        return <MenuItem value={scoreValue}>{scoreValue}</MenuItem>
      })}
    </Select>
    <TextField
      sx={{ width: 300 }}
      label="Name"
      variant="filled"
      placeholder="Name"
      disabled={disabled}
    />
  </FormGroup>
</FormControl>

The TextField is composed of several components, including an Input and a FormControl. The TextField did not inherit the disabled state from the initial FormControl, but instead had to use its own disabled prop to enter a disabled state.

To control both FormControls at the same time, I created a const named disabled. React useState would be a good way to control this value.

MUI FormControl disabled state
MUI FormControl disabled state

Resources

MUI FormControl API

Check out the additional posts on Form components:

Share this post:

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.