React Form with Textarea
A textarea with a live character counter is one of the most requested form patterns — it sets user expectations about message length and prevents submit-time errors. The counter turning red as the user approaches the limit provides a natural warning without interrupting their flow. Disabling resize prevents layout-breaking behaviour in constrained form layouts.
The challenge
Textareas often require custom styling and character count validation that the native element does not provide out of the box.
- Updating the character count on every keystroke without causing performance issues
- Choosing the right max length — enforcing it with maxLength vs showing a warning and blocking submit
- Preventing the textarea from being resized outside its container, which breaks form layouts
- Differentiating a textarea that is full from one that has never been touched for validation purposes
Working code example
Here is a complete, self-contained working example you can drop directly into any React project. It uses Tailwind CSS for styling and requires no external dependencies beyond React itself.
import { useState } from 'react';
const MAX_CHARS = 500;
const WARN_AT = 450;
export default function TextareaWithCounter() {
const [message, setMessage] = useState('');
const [error, setError] = useState('');
const [submitted, setSubmitted] = useState(false);
const remaining = MAX_CHARS - message.length;
const isWarning = message.length >= WARN_AT;
const isOver = message.length > MAX_CHARS;
const handleChange = (e) => {
setMessage(e.target.value);
if (error) setError('');
};
const handleSubmit = (e) => {
e.preventDefault();
if (!message.trim()) { setError('Message cannot be empty'); return; }
if (isOver) { setError(`Message is too long. Remove ${-remaining} characters.`); return; }
setSubmitted(true);
};
if (submitted)
return (
<div className="max-w-md mx-auto p-6 bg-green-50 rounded-2xl text-green-700">
<p className="font-semibold">Message submitted!</p>
<p className="text-sm mt-1 text-gray-600">{message}</p>
</div>
);
return (
<form onSubmit={handleSubmit} className="max-w-md mx-auto p-6 bg-white rounded-2xl shadow space-y-4">
<h2 className="text-xl font-bold text-gray-800">Write a message</h2>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Message
</label>
<textarea
value={message}
onChange={handleChange}
rows={6}
style={{ resize: 'none' }}
placeholder="Write your message here…"
className={`w-full border rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-teal-500 ${
error || isOver ? 'border-red-400' : 'border-gray-300'
}`}
/>
<div className="flex items-center justify-between mt-1">
<span className="text-xs text-red-500">{error}</span>
<span className={`text-xs font-mono ${isOver ? 'text-red-600 font-bold' : isWarning ? 'text-orange-500' : 'text-gray-400'}`}>
{remaining} / {MAX_CHARS}
</span>
</div>
</div>
<button type="submit" disabled={isOver}
className="w-full bg-teal-600 text-white font-semibold py-2 rounded-lg hover:bg-teal-700 transition disabled:opacity-50 disabled:cursor-not-allowed">
Send Message
</button>
</form>
);
}How ReactForm.co helps
ReactForm's visual builder handles all of the above — textarea configuration, validation rules, state management, and responsive layout — without writing a single line of code. Drag fields onto the canvas, configure their properties in the sidebar, and get production-ready React output. You can publish the form and collect responses instantly, or export the JSX to drop into your own codebase.
Build this form visuallyFrequently asked questions
How do I add a character counter to a textarea in React?
Track the textarea value in state and derive the count from value.length. Display MAX - value.length to show remaining characters. Apply conditional styles when the count is near the limit. Decide whether to enforce the limit hard with the maxLength attribute or softly with a warning and blocked submit — soft enforcement gives users more feedback.
How do I prevent a textarea from being resized by the user?
Add style={{ resize: "none" }} directly or use the Tailwind class resize-none. You can also use resize-y to allow vertical-only resizing or resize-x for horizontal only. Disabling resize entirely is the right choice when the textarea is part of a fixed-height form layout.
Should I use maxLength on the textarea or enforce the limit in JavaScript?
Using the maxLength attribute prevents the user from typing beyond the limit but gives no visual feedback about how close they are. Enforcing it in JavaScript lets you show a live counter, a warning color, and a clear message. For best UX, use the JavaScript approach and skip maxLength — or use both: let the UI warn and maxLength as a final browser-level stop.
Related topics
Build this form visually — no code needed
ReactForm.co handles textarea fields, validation, conditional logic, and responsive layout automatically. Publish in minutes and collect responses for free.

