React Material UI Label Overlaps with Text
Asked Answered
I

15

80

I'm using Material UI in a React application and trying to implement an entity update page. In this, we would need to set the exiting values of the entity to let the user update them. The existing values have been set using the defaultValue attribute of the input fields:

<div className="input-field">
   <input type="text" name="name" ref="name" defaultValue={this.state.name} />
   <label htmlFor="name" >Name</label>
</div>

With this approach, the intended functionality works fine. However, the labels of all text fields overlaps with their values. Please see below screenshot:

enter image description here

If I click on each field, the label moves up as expected. However, at the page load, labels do not move up. I tried using the value attribute of the input fields together with the onChange() event handler but experienced the same problem. Really, appreciate your thoughts on this.

The complete source code of this application can be found here: https://github.com/imesh/react-examples/tree/master/meetups/meetups-web-client

This particular page can be found here: https://github.com/imesh/react-examples/blob/master/meetups/meetups-web-client/src/components/UpdateMeetup.js

Github Issue: https://github.com/Dogfalo/materialize/issues/5995

Intermezzo answered 20/6, 2018 at 19:21 Comment(5)
could you confirm you see the same glitch in this sandbox codesandbox.io/s/rwyj71lvx4 ?Junia
Thanks Evgeny for the quick response! Yes, I could see the same issue in the above sandbox. However, I found a workaround. Adding class active to the labels solved the problem. Thanks!Intermezzo
Still I see the problem when the page is refreshed or rather if directly opened the update page URL. The problem does not occur if navigated through a React Link.Intermezzo
I can see correct labels in chrome and ff on windows. can it be browser related?Junia
So is it materialize or material-ui?Objurgate
H
128

This is due to the undefined state of the value.

This workaround works for me as a fallback:

value= this.state.name || '';

e.g. for Material-UI

<div className="input-field">
   <input type="text" name="name" ref="name" value={this.state.name || ''} />
   <label htmlFor="name">Name</label>
</div>
Hebdomadal answered 16/1, 2019 at 22:11 Comment(12)
Could you please be more precise and write where should this piece of code be added in the first place.Ecklund
Your suggestion is not clear as the constant value is not used in the code snippet.Servomotor
@223seneca, I removed the const and added the example in the input example.Hebdomadal
By the way, there is a warning for the value being undefined in the console. I'm here because I searched Stack Overflow before looking at my console output. Because there's a warning in the console I tend to avoid using the || '' pattern and finding the root cause of why it's being passed undefined. The latest reason was because in some old code I had written I had passed {} instead of just not rendering the parent component when a JSON object wasn't available.Posy
It seems that if the "value" is null when the page first renders, this problem occurs and stays, even after the value becomes a valid string, later on. Seems like a bug.Stinson
In my case with a Select, the label overlapped because I was passing [] as children. I changed it to null and that fixed the overlap.Cue
I would just like to add to this to say you probably want a nullish coalescing operator ?? instead of || because you may want a falsy valueBiramous
To explain the fix - The Material UI element can either be "controlled" - you're in charge of reading and setting the value - or "uncontrolled" in which the element stores its state internally, and you query it through a ref. When the element first renders, if its value is undefined it is deemed "uncontrolled", but if it has a value it is Controlled. Passing in an empty string instead of undefined ensures the element is Controlled as we want itAscension
Warning if your field is numeric: this solution will replace 0 (zero) with empty string.Rutkowski
I don't like this solution, it causes a performance hit because the value is a state value https://mcmap.net/q/237806/-react-input-onchange-lagAutomotive
this caused my input field to stop receiving input, i.e. returning to empty ('') on change (btw it had a change handler that touched state, so it makes sense). so this may be an OK-ish workaround for some cases, but the other answers using shrink are more stable.Refugiorefulgence
OUTSTANDING answer! Worked perfectly for me. Even though I always have data to display, it seems to remember the null state from the initial render and stubbornly refuses to behave. Thanks very much!Metallography
T
73

InputLabel has a prop shrink. you can use in TextField like below:

<TextField
  // ... rest
  InputLabelProps={{ shrink: true }}  
/>

OR

<TextField
  // ... rest
  InputLabelProps={{ shrink: !!this.state.name }}
/>
Theorize answered 30/11, 2020 at 8:52 Comment(3)
Thats very useful for date fields without a default value as wellDurable
another useful property to work together with 'shrink' is the 'notched' if you're using OutlineInput together with 'InputLabel'Villasenor
Problem with this is it removes the label from the input fiedl for meIncondite
L
24

I fixed it by adding the condition on shrink based on a text field value.

Just add this code in the text field: Updated (2022)

InputLabelProps={{ shrink: field.value }}  

Example:

<Controller
name="formatted_phone_number"
control={control}
render={({ field }) => (
  <TextField
    {...field}
    className=""
    id="formatted_phone_number"
    label="Phone"
    type="text"
    variant="outlined"
    fullWidth
    InputLabelProps={{ shrink: field.value }}  
  />
)}
/>
Lizarraga answered 28/1, 2022 at 6:57 Comment(2)
I get an error saying 'field' is not defined...Bobbinet
Maybe use InputLabelProps={{shrink: true}} to force itViridescent
H
8

I solved this by using a condition if it is not null && undefined then assign the value otherwise "". Here use the Formik

<TextField
  type="text"
  label="Ending Month"
  variant="outlined"
  fullWidth
  size="small"
  name="endingMonth"
  value={values.endingMonth ?? ""}
  helperText={touched.endingMonth && errors.endingMonth}
  error={Boolean(touched.endingMonth && errors.endingMonth)}
/>
Helli answered 12/4, 2020 at 14:4 Comment(2)
it's enough to check '== null' with double equals.Object
You can shorten checking for null and undefined by using nullish coalescing: values.endingMonth ?? ''Butyrin
H
5

I had the same issue; however it was inconsistent - meaning sometimes I have the labels displayed properly and sometimes overlapped

I tried the following and it worked fine. Basically the form is first rendered empty without data; then the useEffect was fetching the data and populating the data. I set a isLoading state variable - this will be initially set to true and set to false after the data is fetched by API.

Display all the data only after isLoading is false - this works well.

Code Snippet

export default function UserProfile(props) {
const classes = useStyles();
const [user, setUser] = React.useState({});
const [isLoading, setIsLoading] = React.useState(true);

const getUser = async () => {
    const requestOptions = {
        method: 'GET',
        cache: 'no-cache',
        headers: { 
            'Content-Type': 'application/json',
        },
        redirect: 'follow',
        referrerPolicy: 'no-referrer',            
    };

    const response = await axios.request(
        "/api/userprofile",
        requestOptions
    );

    const responseData = await response.data;
    
    setUser( responseData.userProfile );
    setIsLoading(false);
}

React.useEffect(() =>{
    getUser();
    console.log(user);
})

return(
    <div style={{padding:"10px"}}>
        <Grid container className={classes.card}>
            <Container component="main" maxWidth="xs">
            <>{ isLoading ? <div> </div> : 
            <div className={classes.paper}>
                <Typography variant="h6">User Profile</Typography>
                <TextField
                    key="Name"
                    variant="outlined"
                    margin="normal"
                    fullWidth
                    id="Name"
                    label="Name"
                    value={user.name}
                    InputProps={{
                        readOnly: true,
                    }}
                />
                <TextField
                    variant="outlined"
                    margin="normal"
                    fullWidth
                    id="email"
                    label="Email Address"
                    value={user.email}
                    InputProps={{
                        readOnly: true,
                    }}
                />
                <TextField
                    variant="outlined"
                    margin="normal"
                    fullWidth
                    id="phone"
                    label="Phone"
                    value={user.phone_number}
                    InputProps={{
                        readOnly: true,
                    }}
                />
            </div>
            }</>
            </Container>
        </Grid>
    </div>
);

}

Haggadah answered 26/10, 2020 at 17:6 Comment(1)
This solved the issue I was facing as well, setting the default value of loading to True solve the problemPrevailing
S
3

It's 2022 and I still have this error; I have changed the wrapper element around TextField component from grid to div and it solved the issues for me.

Schuster answered 19/4, 2022 at 22:9 Comment(2)
Thank you. This addresses the issue for me on MUI v5.8Shelby
I have div but it didn't work for meIncondite
M
3

I have been trying to resolve this for my login page and it got resolved using these. Just add : For Username :

<TextField
// ... rest
 InputLabelProps={{ shrink: true }}
 />

For Password Feild :

<InputLabel shrink={true}
>Password</InputLabel>
Microelectronics answered 30/6, 2023 at 11:19 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Yuu
F
2

I had this problem but I solved it by adding shrink

return (
    <Controller
      name={name}
      control={control}
      defaultValue={defaultValue || ''}
      render={({ field, fieldState: { error } }) => (
        <TextField
          {...field}
          select
          fullWidth
          label={label}
          value={field.value || ''}
          SelectProps={{ native: true, defaultValue: defaultValue || '' }}
          InputLabelProps={{ shrink: field.value }}
          error={!!error}
          helperText={error?.message}
          {...other}
        >
          {children}
        </TextField>
      )}
    />
  );
Fontes answered 4/10, 2023 at 6:13 Comment(0)
B
1
**Solution 1:** Set Shrink Attribute Based on Field Value if you want to move the label to outline only when it has value

  <TextField
   **key="title"**
   required
   id="title"
   label="Story Title"
   name="title"
   fullWidth
   value={this.state.title}
   InputLabelProps={{
   shrink: this.state.title?true:false,
   }}
/>

Solution 2: Set Shrink attribute as true If you are fine to display the label always at the outline of TextBox 

<TextField
          id="outlined-number"
          label="Number"
          type="number"
          **InputLabelProps={{
            shrink: true,
          }}**
        />


   
Badillo answered 19/1, 2023 at 2:54 Comment(1)
the ?true:false part may be omitted in most casesRefugiorefulgence
R
0

I can say whatever works for me try this out .

This is for Functional based Component.

 const Example = () =>{
   const [name, setName] = useState("");
    const editName = (name) =>{
    setName(name);
  }

        return(
          <TextField
            required
            variant="outlined"
            margin="normal"
            fullWidth
            value={name}
            onChange={e => setName(e.target.value)}
            label="Enter Name"
          />
      )
  }
Renell answered 5/4, 2020 at 18:34 Comment(0)
F
0

Below worked:

<InputLabel shrink={true}>Select A Role</InputLabel>

InputLabelProps gave error in functional component in react.

Fever answered 21/2, 2021 at 5:30 Comment(0)
A
0

Utilizing InputLabelProps={{shrink: true}}, you can add a state and an onSelect to toggle the shrink state

const [shrink1, setShrink1] = useState(false)

<TextField
            fullWidth
            id='filled-basic'
            label='Name'
            variant='filled'
            value={formState.name}
            onChange={(event) => setFormState({ name: event.target.value })}
            onSelect={() => setShrink1(true)}
            InputLabelProps={{ shrink: shrink1 }}
          />

Adessive answered 14/7, 2021 at 3:51 Comment(0)
B
0

My RHF hook for MUI:

// hooks.js
function useMUI(useFormObject) {
  const register = (name, options) => ({
    ...useFormObject.register(name, options),
    InputLabelProps:{ shrink:!!useFormObject.watch(name) }
  });
  return {
    ...useFormObject,
    register
  };
}

Usage example:

const { register, handleSubmit } = useMUI(useForm());

return (
  <TextField label="First name" {...register('firstName')} />
);

Result: enter image description here

Bennion answered 12/2, 2023 at 17:1 Comment(0)
H
0

I made it work by setting the value to null for the first render when the state value is undefined.

Here's an example:

          <TextField
              label={errors.email || "Email"}
              name={'adminEmail'}
              error={!!errors.email}
              onChange={handleChange}
              value={values.email || null}
              variant="outlined"
              className={`mb-8 `}
              autoComplete="new-password"
          />
Hardwick answered 12/4, 2023 at 8:53 Comment(0)
B
0

After lot research I found solution. In input styles please use marginTop:10, Below I have added code snippet.

    <TextInput
      label="Description"
      variant="standard"
      value={descTextValue}
      inputStyle={{            
        marginTop:10
      }}
      onChangeText={value => setDescTextValue(value)}
      color="#B3B3B3"
      multiline
      numberOfLines={4}
      maxLength={250}
    />

I hope it will works.

Bleachers answered 5/7, 2023 at 12:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.