Max Schmitt

December 18 2022

React: How to Create an Autogrowing Textarea

An autogrowing textarea is a textarea element that automatically adjusts its height to the number of lines the user entered.

Here is a textarea component written for React that automatically adjusts its height no matter if it's controlled or uncontrolled.

Usage

JS

<Textarea autoHeight rows={4} />

Implementation

TS

interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
autoHeight?: boolean
}
function Textarea({
value: valueFromProps,
onChange: onChangeFromProps,
defaultValue,
autoHeight = false,
rows: rowsFromProps = 3,
...rest
}: TextareaProps) {
// Determine if component is used in a controlled or uncontrolled
// fashion.
const isControlled = typeof valueFromProps != 'undefined'
const hasDefaultValue = typeof defaultValue != 'undefined'
const [internalValue, setInternalValue] = useState(hasDefaultValue ? defaultValue : '')
// If component is controlled, take the value from the props,
// otherwise take it from the value we track ourselves.
const value = isControlled ? valueFromProps : internalValue
// The rows attribute is what will determine the height of our
// textarea. Notice how we only automatically determine the
// rows attribute if props.autoHeight is true. In this case
// we also use props.rows to set the minimum number of rows.
const rows = autoHeight ? Math.max(rowsFromProps, String(value).split('\n').length) : rowsFromProps
// This a custom onChange handler that reports any changes
// to props.onChange and also updates our internal value
// in case the component is uncontrolled.
const onChange = useCallback<React.ChangeEventHandler<HTMLTextAreaElement>>(
(e) => {
if (onChangeFromProps) {
onChangeFromProps(e)
}
if (!isControlled) {
setInternalValue(e.target.value)
}
},
[isControlled, onChangeFromProps]
)
return <textarea rows={rows} value={value} onChange={onChange} {...rest} />
}

View the demo on CodeSandbox.