Muhi Logo Text
Work With MeAboutTestimonialsBlog

MUI FormControl: Creating Dynamic and Customized Forms

Learn how to create dynamic, customizable inputs that enhance user experience with React Material UI FormControl.

Last updated on February 14, 2024

react
mui
MUI FormControl

MUI Form Control is a component that enhances form functionality within Material UI. It provides essential context for form inputs, such as their filled, focused, error, and required states, enabling more advanced use cases. By encompassing smaller components like Label, Input, and HelperText within FormControl, developers can create fully customized TextFields, offering precise control over the form’s behavior and appearance.

Here’s a simple code example to illustrate how to integrate smaller components within FormControl to craft a custom TextField:

import React from "react";
import FormControl from "@mui/material/FormControl";
import InputLabel from "@mui/material/InputLabel";
import Input from "@mui/material/Input";
import FormHelperText from "@mui/material/FormHelperText";

export default function CustomTextField() {
  return (
    <FormControl>
      <InputLabel>Email address</InputLabel>
      <Input />
      <FormHelperText>We'll never share your email.</FormHelperText>
    </FormControl>
  );
}

This example demonstrates how FormControl can encapsulate an InputLabel, Input, and FormHelperText to create a cohesive, customized input field with a label and helper text for enhanced user guidance.

In this tutorial, we’ll explore the various features and use cases of MUI FormControl, including its properties, customization options, and advanced form-handling techniques.

Form Control Context

Using the FormControl component as a wrapper provides a context for all child elements, enabling them to access various states such as error, focused, filled, and required. These states can be accessed using the useFormControl hook, offering a powerful way to dynamically adjust the behavior and styling of form elements based on user interaction.

Error State

When an input event triggers an error, the FormControl reflects this state, allowing all child elements to update accordingly. For example, both the helper text and the input field can change color to indicate the error. Additionally, a custom helper text component can access the error state from the useFormControl hook for more advanced usage.

The following example demonstrates how to access the error state using the useFormControl hook:

import React from "react";
import FormHelperText from "@mui/material/FormHelperText";
import Input from "@mui/material/Input";
import FormControl, { useFormControl } from "@mui/material/FormControl";

function CustomHelperText() {
  const { error } = useFormControl() || {};
  return (
    <FormHelperText error={error}>
      {error ? "Error text" : "Helper text"}
    </FormHelperText>
  );
}

export default function InputValidation() {
  const [error, setError] = React.useState(false);

  const handleChange = event => {
    // Custom validation logic here
    setError(event.target.value.length < 3);
  };

  return (
    <FormControl error={error}>
      <Input onChange={handleChange} />
      <CustomHelperText />
    </FormControl>
  );
}

In this example, the CustomHelperText component accesses the error state using the useFormControl hook. When the input value is less than three characters, the error state is set to true, and the helper text changes to an error message. This demonstrates how the FormControl context can create dynamic form elements that respond to user input.

Focused State

The focused state is another important context provided by the FormControl component. When an input field is focused, the FormControl reflects this state, allowing child elements to update their appearance accordingly.

In the following example, we’ll create a custom component that displays a message based on the focused state of the input field:

import React from "react";
import Input from "@mui/material/Input";
import FormControl, { useFormControl } from "@mui/material/FormControl";

function FocusIndicator() {
  const formControl = useFormControl();
  const focused = formControl?.focused || false;
  return <div>{focused ? "Input is focused" : "Input is not focused"}</div>;
}

export default function InputFocus() {
  return (
    <FormControl>
      <Input />
      <FocusIndicator />
    </FormControl>
  );
}

In this example, the FocusIndicator component accesses the focused state using the useFormControl hook. When the input field is focused, the message “Input is focused” is displayed; when it’s not, the message “Input is not focused” is displayed. This demonstrates how the FormControl context can be used to create dynamic form elements that respond to user interaction.

Filled State

The filled state indicates whether the input has a value, allowing for custom styling or behavior when an input field is not empty.

The following example demonstrates how to access the filled state using the useFormControl hook:

import React from "react";
import Input from "@mui/material/Input";
import FormHelperText from "@mui/material/FormHelperText";
import FormControl, { useFormControl } from "@mui/material/FormControl";

function FilledIndicator() {
  const formControl = useFormControl();
  const filled = formControl?.filled || false;
  return (
    <FormHelperText>
      {filled ? "Input has value" : "Input is empty"}
    </FormHelperText>
  );
}

export default function InputFilled() {
  return (
    <FormControl>
      <Input defaultValue="" />
      <FilledIndicator />
    </FormControl>
  );
}

In this example, the FilledIndicator component accesses the filled state using the useFormControl hook. When the input field has a value, the message “Input has value” is displayed, and when it’s empty, the message “Input is empty” is displayed.

Required State

The required state can be used to indicate that an input is mandatory, providing visual cues or validations to ensure users fill out essential fields.

The following example demonstrates how to access the required state using the useFormControl hook:

import React from "react";
import Input from "@mui/material/Input";
import Stack from "@mui/material/Stack";
import FormHelperText from "@mui/material/FormHelperText";
import FormControl, { useFormControl } from "@mui/material/FormControl";

function RequiredIndicator() {
  const formControl = useFormControl();
  const required = formControl?.required || false;
  return (
    <FormHelperText>
      {required ? "This field is required" : "This field is optional"}
    </FormHelperText>
  );
}

export default function InputRequired() {
  return (
    <Stack sx={{ width: "200px" }}>
      <FormControl>
        <Input />
        <RequiredIndicator />
      </FormControl>
      <FormControl required>
        <Input />
        <RequiredIndicator />
      </FormControl>
    </Stack>
  );
}

In this example, the RequiredIndicator component accesses the required state using the useFormControl hook. When the input field is marked as required, the message “This field is required” is displayed, and when it’s optional, the message “This field is optional” is displayed.

Customizing TextField Appearance

A TextField in Material UI comprises smaller components, allowing extensive customization and styling flexibility. When wrapped with a FormControl, these components can be individually styled and manipulated to fit any design requirement. The primary components involved are:

  • FormLabel: Provides a label for the input.
  • FormHelperText: Offers additional hints or error messages.
  • Input: The actual input field where the user types.
  • InputLabel: Acts as a label specifically for the Input component, offering more integration with Material UI’s style system.

By customizing these components, you can achieve high styling customization. Here’s an example showcasing how to use all these components together for complete styling flexibility:

import React from "react";
import FormControl from "@mui/material/FormControl";
import Input from "@mui/material/Input";
import InputLabel from "@mui/material/InputLabel";
import { styled } from "@mui/material/styles";

const CustomInput = styled(Input)({
  "&.MuiInput-root": {
    margin: 0,
  },
  "& .MuiInputBase-input": {
    borderRadius: 5,
    position: "relative",
    border: "1px solid #ced4da",
    padding: "10px 12px",
  },
});

const CustomInputLabel = styled(InputLabel)({
  position: "relative",
  transform: "none",
  marginBottom: "5px",
});

export default function CustomInputComponent() {
  return (
    <FormControl variant="standard">
      <CustomInputLabel htmlFor="custom-input">Email</CustomInputLabel>
      <CustomInput id="custom-input" disableUnderline />
    </FormControl>
  );
}

In this example, we’ve used the styled function from Material UI to create custom styles for the Input and InputLabel components. By applying these styles, we’ve achieved a custom appearance for the input field and label, including a border radius, border color, and spacing between the label and input.

Conclusion

Throughout this tutorial, we’ve explored the versatile nature of Material UI’s FormControl and its ability to provide context and enhance the functionality of form inputs. By understanding and utilizing the FormControl component, you can create more responsive, accessible, and styled forms.

With a detailed explanation of its context management, through states like error, focused, filled, and required, and the ability to fully customize each part of the TextField, we’re now equipped to build complex forms that are both user-friendly and adhere to modern design standards.

The complete Material UI documentation is an excellent resource for further exploration of FormControl’s props and capabilities, ensuring that our forms will be functional and seamlessly integrated into our projects.

If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!

Recommended Reading

Learn how to create a scrollable table with a sticky header and column in React.

react
html
css

Discussion

Upskill Your Frontend Development Techniques 🌟

Subscribe to stay up-to-date and receive quality front-end development tutorials straight to your inbox!

No spam, sales, or ads. Unsubscribe anytime you wish.

© 2024, Muhi Masri