Understanding MUI Labels: TextField Labels, Input Labels, and Form Labels

Material-UI labels provide visual information in a UI, but understanding the different label use cases can be challenging. For example, a TextField is composed of an InputLabel component plus other components. FormLabels are often used as part of a subcomponent in a form. In this article we will explore when to use each label and how to precisely style and position them.

Below is a screenshot of the UI we will create:

MUI Form Label, TextField Label, and Input Label

A Code Sandbox link with a live demo is in the Resources section. Full code for this article is also in the Resources section.

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

MUI InputLabel vs. MUI FormLabel

It’s difficult to remember the differences in InputLabel, FormLabel, and which kind is used in a TextField. Here’s the hierarchy:

  • An InputLabel is a specific kind of FormLabel. It has class MuiInputLabel-root and MuiFormLabel-root applied in the DOM
  • A FormLabel only has class MuiInputLabel-root applied and so is not an InputLabel
  • An InputLabel component is both a standalone component and is a compositional component inside a TextField.

With that said, what are the differences between an InputLabel and a FormLabel? Take a look at the screenshot. We can see the FormLabel above the Radio, while the InputLabel (in a TextField) is highlighted in the DOM.

MUI InputLabel in a TextField
MUI InputLabel in a TextField

It turns out these two components are very similar:

  • They both render as a label element
  • When using TextField or wrapping an InputLabel in a FormControl, the InputLabel and the FormLabel both have class MuiFormLabel-root applied.

This second point is critical. A lot of default styling and animation is applied to the InputLabel when it has MuiFormLabel-root class applied.

The differences are that the InputLabel has less default styling when it is not wrapped in a FormControl, and it has more default styling and animation when it is. This is accomplished through additional default classes being applied. In other words, an InputLabel inside a FormControl is the same as a FormLabel plus it has additional styling.

We’ll explore customizing the styling for each below.

MUI TextField Label Style

The FormControl component that wraps the composing components of the TextField adds a lot of default behavior. Below is a TextField and a FormControl/InputLabel/Input combo.

Unstyled MUI TextField Label and Input Label (inside a FormControl component)

Compare this to an InputLabel/Input combo with no FormControl:

MUI InputLabel
MUI InputLabel

I’ll show how to add some custom styling to both components below. Full code for the demo will be in the Resources section.

Here’s how to style TextField border color.

MUI TextField Label Color

There are a couple of options for selecting the TextField label with nested CSS selectors. I chose to target the label element that I saw in the DOM, but I could also have targeted the class MuiInputLabel-root.

<TextField sx={{"& label": {color: "secondary.main"}}}/>

After targeting the label element, I simply styled the color property.

Here’s how to style TextField’s text color, alignment, width, and height.

MUI TextField Label Margin

The margin-left property might give you the ability to position the label as desired within a TextField.

Adding margin to the TextField label was trickier because there is a default animation when the user clicks into the Input. One option is to remove the animation, the other is to remove the margin on focus. I decided to have no margin on focus.

<TextField 
  sx={{
    "& label": {
      marginLeft: tfValue ? 0 : "65%"
      "&.Mui-focused": {
        marginLeft: 0
      }
    }
  }}
/>

With this code I set marginLeft to 65% because that worked well with the length of my label. A better plan might be to get a ref to the width of the TextField and take a percent of that value, or take a percent of a CSS width value for TextField.

After a user clicks, the Mui-focused class is automatically added to the TextField. I targeted the label plus that class in order to strip margin when the label performs its default animation of moving up on focus.

Once the user enters a value, then the value is populated in the state value tfValue and the margin left is set to 0px.

MUI TextField Label Removed

If you want to remove the label from a TextField, simply don’t pass a value in the label prop.

If you do want a label value, but you don’t want the label visible when the TextField is focused (i.e. the user is typing in it), then you need a more complex CSS solution shown below.

<TextField 
  sx={{
    "& label.Mui-focused": {
      display: "none"
    },
    "& legend": {
      display: "none"
    }
  }}
/>

CSS property display: none will remove the element from the UI. It won’t take up any space, but will still be visible in the DOM.

The legend only needs to be set to display: none if the TextField variant is outlined. This will remove the ‘notch’ from the outline on focus.

MUI InputLabel Color

The Material-UI InputLabel is easier to style than the TextField label because it is a standalone component.

<InputLabel sx={{color: "red"}}>Input Label Text</InputLabel>

If you compose your own input using FormControl/InputLabel/Input components, you can style each piece independently. It likely would be easier for a beginner to style, but it requires more code than using a pre-composed TextField.

MUI FormLabel Style

Directly styling a Material-UI FormLabel is easily accomplished with the sx prop. This is because we usually directly use the FormLabel instead of having it be a compositional component.

Here’s a guide to every MUI Form component.

MUI FormLabel Color

The color property can be directly customized in the FormLabel, just like with the InputLabel.

<FormLabel id="radios" sx={{color: "rgba(200, 132, 39, .8)"}}>
Label Text</FormLabel>

In this example I set a rgba value for the color. Notice I also set an id that is relevant to the id of the RadioGroup that the FormLabel supports in my form example.

MUI FormLabel Postion

Most UIs will be something like the following:

<FormControl>
  <FormLabel/>
  <RadioGroup/>
  <FormHelperText/>
</FormControl>

This naturally stacks the FormLabel vertically to the top of the next component.

If you desire for the FormLabel to be to the left or right of an adjacent form component, you need to wrap the layout in a FormGroup component. The FormGroup component has a row prop that takes a boolean value. This will lay out children components vertically or horizontally.

Read a detailed guide to MUI form layout here.

MUI FormLabel Overflow

If you have long text in your FormLabel there are a variety of overflow options to pursue. I’ll give an example of adding ellipses below:

<FormLabel 
  id="radios" 
  sx={{
    maxWidth: 200, 
    whiteSpace: "nowrap", 
    textOverflow: "ellipsis", 
    overflow: "hidden"
  }}
>
  Long Form Label Text Example
</FormLabel>

I set maxWidth: 200 to force an overflow scenario. Here’s what each remaining CSS property accomplishes when the content is too wide.

  • overflow: “hidden” – enforces the width of the container and won’t let content go beyond it. Content is free to wrap.
  • whiteSpace: “nowrap” – enforces a single line of text, but text can expand beyond the width of the container.
  • textOverflow: “ellipses” – adds an ellipses in situations where text is truncated.

Using overflow: "hidden" and whitespace: "nowrap" together will simply truncate the text. textOverflow: "ellipses" won’t do anything unless text is truncated.

Here’s the final styling in our label overflow scenario:

MUI FormLabel Overflow
MUI FormLabel Overflow

Resources

Here’s an easy way to set FormLabel background color with sx, and here’s The Ultimate Guide to Material-UI FormControl.

Here’s the full code from the working example:

import * as React from "react";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import FormGroup from "@mui/material/FormGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormHelperText from "@mui/material/FormHelperText";
import FormLabel from "@mui/material/FormLabel";
import TextField from "@mui/material/TextField";
import Input from "@mui/material/Input";
import InputLabel from "@mui/material/InputLabel";
import Stack from "@mui/material/Stack";

const formLabelStyling = {
  color: "rgba(200, 132, 39, .8)",
  maxWidth: 200,
  whiteSpace: "nowrap",
  textOverflow: "ellipsis",
  overflow: "hidden"
};

const textfieldStyling = {
  "& label": {
    //display: "none",
    //visibility: "hidden",
    //whiteSpace: "normal",
    color: "secondary.main",
    width: 100,
    "&.Mui-focused": {
      marginLeft: 0
      //display: "none"
    }
  }
  // "& legend": {
  //   display: "none"
  // }
};

const inputStyling = {
  color: "red"
};

export default function ErrorRadios() {
  const [tfValue, setTFValue] = React.useState("");
  console.log(tfValue);
  return (
    <>
      <form style={{ maxWidth: "400px" }}>
        <Stack>
          <FormControl sx={{ m: 2 }} variant="standard">
            {/* <FormGroup row={true}> */}
            <FormLabel id="radios" sx={formLabelStyling}>
              Long Form Label Text Example
            </FormLabel>
            <RadioGroup aria-labelledby="radios" name="quiz">
              <FormControlLabel
                value="v1"
                control={<Radio />}
                label="Radio 1"
              />
              <FormControlLabel
                value="v2"
                control={<Radio />}
                label="Radio 2"
              />
            </RadioGroup>
            <FormHelperText>Form Helper Text</FormHelperText>
            {/* </FormGroup> */}
          </FormControl>
          <TextField
            label="TextField Label Text"
            value={tfValue}
            onChange={(newValue) => setTFValue(newValue.target.value)}
            sx={{
              marginBottom: 2,
              ...textfieldStyling,
              "& label": { marginLeft: tfValue ? 0 : "65%" }
            }}
          />
          <InputLabel sx={inputStyling}>Input Label Text</InputLabel>
          <Input sx={{ marginTop: 4 }} />
        </Stack>
      </form>
      <br />
      <a
        target="_blank"
        href="https://smartdevpreneur.com/mui-labels-textfield-labels-input-labels-and-form-labels/"
      >
        Click here for more Material UI Label styling
      </a>
    </>
  );
}

Code Sandbox Link

MUI FormLabel API Docs

MUI InputLabel API Docs

MUI TextField Docs

Share this post:

6 thoughts on “Understanding MUI Labels: TextField Labels, Input Labels, and Form Labels”

  1. Thanks for the great article, Jon! It really helped me to understand a few key bits I was missing about styling MUI stuff.

    I had issues with hiding the label for one of my filled text fields (the label would occupy space even though I was using correct selectors and display: none), and this is what did it in the end: https://github.com/mui/material-ui/issues/23738#issuecomment-734948728

    basically there’s a not-so-well-documented prop called hiddenLabel that does all the “heavy lifting” (aka kills the label with fire) for you.

    Reply
    • Hi Lidia, thanks for the helpful comment! That prop is a great find. MUI is always growing and sometimes there are features that can only be found by digging deep in their github issues.

      Reply
    • Good catch, that’s a problem. The easiest solution is to apply marginLeft: 0 when the TextField has a value. This can be done by adding a state prop. I updated the code in the sandbox and the post.

      Reply

Leave a Comment

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