Skip to content

Home

Create a textarea React component with word or character limit

The <textarea> element is a multi-line text input control that allows users to enter text. React allows you to enhance its functionality by creating custom components that can have additional features, such as a word or character limit.

Uncontrolled textarea element

The simplest version of a <textarea> component is an uncontrolled one. It uses the defaultValue prop passed down from the parent as the initial value of the input field. The onChange event is used to fire the onValueChange callback and send the new value to the parent.

const TextArea = ({
  cols = 20,
  rows = 2,
  defaultValue,
  onValueChange,
  ...rest
}) => {
  return (
    <textarea
      cols={cols}
      rows={rows}
      defaultValue={defaultValue}
      onChange={({ target: { value } }) => onValueChange(value)}
      {...rest}
    />
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <TextArea
    placeholder="Insert some text here..."
    onValueChange={val => console.log(val)}
  />
);

Controlled textarea element

A controlled <textarea> component instead uses the value prop to manage the input field's value. The onChange event is used to update the value prop with the new value. Taking this a step further, we can use the useState() hook to create a state variable, content. For this variant, it won't do much more than mirror the value prop.

const TextArea = ({ cols = 20, rows = 2, value, onChange, ...rest }) => {
  const [content, setContent] = React.useState(value);

  return (
    <textarea
      cols={cols}
      rows={rows}
      value={content}
      onChange={event => {
        setContent(event.target.value);
        onChange(event.target.value);
      }}
      {...rest}
    />
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <TextArea
    placeholder="Insert some text here..."
    value="Hello, world!"
    onChange={val => console.log(val)}
  />
);

Limited character textarea

Expanding upon the controlled <textarea> component, we can add a character limit to it. The useState() hook is used to create the content state variable, which is set to the value prop trimmed down to limit characters. A method, setFormattedContent, is created to trim the content down to limit characters and memoize it using the useCallback() hook. The onChange event of the <textarea> is bound to call setFormattedContent with the value of the fired event.

const LimitedTextarea = ({ rows, cols, value, limit }) => {
  const [content, setContent] = React.useState(value.slice(0, limit));

  const setFormattedContent = React.useCallback(
    text => {
      setContent(text.slice(0, limit));
    },
    [limit, setContent]
  );

  return (
    <>
      <textarea
        rows={rows}
        cols={cols}
        onChange={event => setFormattedContent(event.target.value)}
        value={content}
      />
      <p>
        {content.length}/{limit}
      </p>
    </>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <LimitedTextarea limit={32} value="Hello!" />
);

Limited word textarea

Similarly, we can create a <textarea> component with a word limit. The implementation is much the same as before, except that we use String.prototype.split() to turn the input into an array of words. The setFormattedContent method is then used to trim the input down to limit words.

const LimitedWordTextarea = ({ rows, cols, value, limit }) => {
  const [{ content, wordCount }, setContent] = React.useState({
    content: value,
    wordCount: 0
  });

  const setFormattedContent = React.useCallback(
    text => {
      let words = text.split(' ').filter(Boolean);
      if (words.length > limit) {
        setContent({
          content: words.slice(0, limit).join(' '),
          wordCount: limit
        });
      } else {
        setContent({ content: text, wordCount: words.length });
      }
    },
    [limit, setContent]
  );

  React.useEffect(() => {
    setFormattedContent(content);
  }, []);

  return (
    <>
      <textarea
        rows={rows}
        cols={cols}
        onChange={event => setFormattedContent(event.target.value)}
        value={content}
      />
      <p>
        {wordCount}/{limit}
      </p>
    </>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <LimitedWordTextarea limit={5} value="Hello there!" />
);

More like this

Start typing a keyphrase to see matching snippets.