material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

[material-ui][TextField] When with minRows, it makes the height from small to final height

Open tjx666 opened this issue 1 year ago • 15 comments

Steps to reproduce

  1. open https://stackblitz.com/edit/nextjs-ziwkdm?file=package.json
  2. click refresh multiple time

you will see the height of textarea change from:

image

to:

image

Current behavior

The height will change from a small height to the final height

Expected behavior

No height change, it will make web page cls metric bad

Context

fix the web page cls, you can see the prompt input box height change:

https://github.com/mui/material-ui/assets/41773861/b1b01e6f-ea0b-4015-b33b-c8540849d9ca

Your environment

npx @mui/envinfo
   System:
    OS: macOS 14.5
  Binaries:
    Node: 20.13.1 - ~/.local/state/fnm_multishells/51935_1717493153731/bin/node
    npm: 10.8.1 - ~/.local/state/fnm_multishells/51935_1717493153731/bin/npm
    pnpm: 9.1.0 - ~/.local/state/fnm_multishells/51935_1717493153731/bin/pnpm
  Browsers:
    Chrome: 125.0.6422.142
  npmPackages:
    @emotion/react: ^11.11.4 => 11.11.4 
    @emotion/styled: ^11.11.5 => 11.11.5 
    @mui/material: ^5.15.19 => 5.15.19 
    @types/react: ^18 => 18.2.75 
    react: ^18 => 18.2.0 
    react-dom: ^18 => 18.2.0 
    typescript: ^5 => 5.4.5 

Search keywords: TextField, minRows, height

tjx666 avatar Jun 04 '24 09:06 tjx666

@ZeeshanTamboli do you know if this might be related to the recent TextareaAutosize fixes?

DiegoAndai avatar Jun 07 '24 21:06 DiegoAndai

do you know if this might be related to the recent TextareaAutosize fixes?

Doesn't look related to the recent TextareaAutosize fixes. And I cannot reproduce the issue:

https://github.com/mui/material-ui/assets/20900032/a68dbed2-be67-42f5-8d50-f896da7aaeba

ZeeshanTamboli avatar Jun 08 '24 11:06 ZeeshanTamboli

@ZeeshanTamboli Try open the devtools and disable cache.

image

In your video, 0.01 to 0.03 reproduce the issue, check your video carefully

tjx666 avatar Jun 08 '24 12:06 tjx666

@ZeeshanTamboli You can more easily to reproduce by using performance throttle to slow down performance:

image

tjx666 avatar Jun 11 '24 19:06 tjx666

I can repro with throttling x6. Accepting as a bug and adding the ready-to-take label.

DiegoAndai avatar Jun 12 '24 15:06 DiegoAndai

chrome126 support throttling x20 😄

tjx666 avatar Jun 15 '24 11:06 tjx666

This may be expected behavior. The field cannot know how tall the rendered rows of text will be without rendering a line of text client-side, which would always result in a CLS problem.

Potential solutions

  • The developer provides a hint to TextareaAutosize about how tall one line would be, which could populate the initial render.
  • TextareaAutosize has an option to not resize until the first user interaction. This eliminates the CLS problem but makes the component "wiggly" to use.
  • Developer uses sx to set a minimum height when CLS matters.

General note

The offending code seems to be around here: https://github.com/mui/material-ui/blob/master/packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx#L71

mi-na-bot avatar Jul 07 '24 21:07 mi-na-bot

Some cases can use minHeight, but sometimes the initial height is large than final actual height.

I had to use maxHeight and remove the limit after after render:

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

export default function MuiTextareaMinRows() {
    const ref = useRef(null);

    const [maxHeight, setMaxHeight] = useState('43px');

    useEffect(() => {
        if (typeof window !== 'undefined') {
            setTimeout(() => {
                setMaxHeight('unset');
            }, 500);
        }
    }, []);

    return (
        <TextField
            sx={{
                'marginTop': 3,
                '& > .MuiInputBase-multiline': {
                    p: '10px 12px',
                },
                maxHeight,
                '& .MuiOutlinedInput-root': {
                    maxHeight,
                },
            }}
            size="small"
            placeholder="Anything you’d like to exclude?"
            multiline
            minRows={1}
            fullWidth
        />
    );
}

tjx666 avatar Jul 10 '24 16:07 tjx666

If I use minHeight the placeholder text will change position:

export default function MuiTextareaMinRows() {
    const [maxHeight, setMaxHeight] = useState('43px');

    useEffect(() => {
        if (typeof window !== 'undefined') {
            setTimeout(() => {
                setMaxHeight('unset');
            }, 500);
        }
    }, []);

    return (
        <>
            <TextField
                multiline
                minRows={4}
                placeholder="e.g. A cat is sitting on a table"
                fullWidth
                sx={{
                    '& > .MuiInputBase-multiline': {
                        minHeight: '149px',
                    },
                }}
            />

            <TextField
                sx={{
                    'mt': 2,
                    '& > .MuiInputBase-multiline': {
                        maxHeight,
                        p: '10px 12px',
                    },
                }}
                size="small"
                placeholder="Anything you’d like to exclude?"
                multiline
                minRows={1}
                fullWidth
            />
        </>
    );
}

https://github.com/mui/material-ui/assets/41773861/8b855b46-bcfe-47da-963b-f3ec27f683c5

image

tjx666 avatar Jul 10 '24 16:07 tjx666

@tjx666 I have handled this in the past by using minHeight in sx and not using minRows. You can set the value in terms of em units with help from lineHeight to be exactly what minRows would be without using the client side JS.

mi-na-bot avatar Jul 10 '24 23:07 mi-na-bot

@mi-na-bot can you provide a piece of simple code to demo?

tjx666 avatar Jul 11 '24 02:07 tjx666

@tjx666 It depends on understanding the styling. There are two cases, whether the line-height is set as a scalar or a value with units. Hope this helps!

https://codesandbox.io/p/sandbox/polished-surf-c5x353

mi-na-bot avatar Jul 11 '24 03:07 mi-na-bot

Thanks your help @mi-na-bot I also decide give up official minRows property and set the minHeight/maxHeight manually

tjx666 avatar Jul 11 '24 08:07 tjx666

The CSS property field-sizing solved the issue for me when the textarea content height is higher from the minimum number of rows (not exactly the same issue as the OP, but the consequence is basically the same, with the textarea "flickering", changing from a smaller height to the correct one).

It must be applied directly to the internal textarea element wrapped by the component:

const textareaCss = css`
	textarea {
		field-sizing: content;
	}
`;

<TextField css={textareaCss} ... />

(The example above uses Emotion CSS, but the same approach works with any CSS solution.)

Previously, the textarea would initially render at the wrong height and then resize to the correct one, causing a brief visual jump.

With this fix, SSR pages now render the textarea at the correct height even with JavaScript disabled.

This solution currently works in Chrome and Edge, but not yet in Firefox.

Compatibility reference: https://caniuse.com/?search=field-sizing

I would expect the minRows to work for cases in which the content size is smaller than when this property is applied, but I see the approach with minHeight the only solution at the moment.

lucasbasquerotto avatar Oct 20 '25 19:10 lucasbasquerotto

Hey, I'm also seeing a height flicker on TextFields and the multiline prop seems to be the only culprit. I made a minimal reproduction showing the height flicker problem as well as my solution to fix it.

The solution is to set the textarea height to the desired initial height (1 line in my case):

<TextField
  multiline
  slotProps={{
    htmlInput: {
      style: {
        height: 23,
      },
    },
  }}
/>

ClementDreptin avatar Nov 24 '25 08:11 ClementDreptin