[material-ui][TextField] When with minRows, it makes the height from small to final height
Steps to reproduce
- open https://stackblitz.com/edit/nextjs-ziwkdm?file=package.json
- click refresh multiple time
you will see the height of textarea change from:
to:
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
@ZeeshanTamboli do you know if this might be related to the recent TextareaAutosize fixes?
do you know if this might be related to the recent
TextareaAutosizefixes?
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 Try open the devtools and disable cache.
In your video, 0.01 to 0.03 reproduce the issue, check your video carefully
@ZeeshanTamboli You can more easily to reproduce by using performance throttle to slow down performance:
I can repro with throttling x6. Accepting as a bug and adding the ready-to-take label.
chrome126 support throttling x20 😄
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
sxto 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
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
/>
);
}
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
@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 can you provide a piece of simple code to demo?
@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
Thanks your help @mi-na-bot I also decide give up official minRows property and set the minHeight/maxHeight manually
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.
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,
},
},
}}
/>