HTML <textarea> reference
Jump to this section
Common attributes Jump to this section
Basic textarea structure Jump to this section
A <textarea> element creates a multi-line text input field. Unlike input elements, textareas can contain multiple lines of text and the content goes between opening and closing tags.
<textarea name="message" rows="4" cols="30">
Default text here
</textarea>name Jump to this section
Identifies the textarea when submitting form data. The name becomes the key, and the textarea content becomes the data.
Use descriptive names that match your backend expectations (e.g., comment, message, description).
<textarea name="comment">User's comment</textarea>
<!-- Submits as: comment=User's comment -->
rows Jump to this section
Specifies the visible height of the textarea in lines of text. This is the initial height; users can typically resize it.
Use to set an appropriate starting size for the expected content length.
<textarea rows="6">
Taller textarea
</textarea>cols Jump to this section
Specifies the visible width of the textarea in average character widths. Modern layouts often use CSS width instead.
Use when you need a specific character-based width, though CSS is generally more flexible.
<textarea cols="50">
Wider textarea
</textarea>placeholder Jump to this section
Displays hint text when the textarea is empty. Disappears when the user starts typing.
Use to provide examples or instructions. Don't rely on it for critical information as it disappears.
<textarea placeholder="Tell us what you think...">
</textarea>maxlength Jump to this section
Limits the maximum number of characters users can enter. The browser prevents additional input once reached.
Use to enforce character limits for comments, tweets, or database field constraints.
<textarea maxlength="100">
</textarea>minlength Jump to this section
Requires a minimum number of characters for form validation. The form won't submit if the content is too short.
Use when you need substantive input (e.g., requiring at least 10 characters for a review).
<textarea minlength="10" required>
</textarea>
required Jump to this section
Boolean attribute that makes the textarea mandatory for form submission. The form won't submit if left empty.
Use on fields that must have content. Pair with clear labels.
<textarea required>
</textarea>disabled Jump to this section
Boolean attribute that makes the textarea non-interactive and excludes it from form submission. Typically displayed with reduced opacity.
Use when the textarea is temporarily unavailable or depends on another action.
<textarea disabled>
Cannot edit this
</textarea>readonly Jump to this section
Boolean attribute that makes the textarea non-editable but still allows selection and copying. Unlike disabled, readonly fields are included in form submission.
Use when you want to display text that users can copy but not modify.
<textarea readonly>
Read-only content
</textarea>autofocus Jump to this section
Automatically focuses the textarea when the page loads. Only one element per page should have this attribute.
Use sparingly on forms where the textarea is the primary action. Can disorient users if overused.
<textarea autofocus>
</textarea>
autocomplete Jump to this section
Controls browser autofill behavior. Can specify what type of data the browser should suggest.
Common values: on, off, name, email, street-address, tel, etc.
<textarea name="address" autocomplete="street-address">
</textarea>
<textarea autocomplete="off">
<!-- Disable autocomplete -->
</textarea>
spellcheck Jump to this section
Controls whether the browser should check spelling. Accepts true or false.
Use false for code editors, usernames, or technical content. Use true for prose.
<textarea spellcheck="true">
Regular text with spell checking
</textarea>
<textarea spellcheck="false">
code_without_spellcheck()
</textarea>
wrap Jump to this section
Controls how text is wrapped when submitting the form.
Values:
soft(default): Text wraps visually but newlines aren't added to submitted datahard: Newlines are added to match visual wrapping (requirescolsattribute)
<textarea wrap="soft">
Text wraps visually only
</textarea>
<textarea wrap="hard" cols="40">
Newlines added on submit
</textarea>
form Jump to this section
Associates the textarea with a form element by its id, even when the textarea is outside the form in the DOM.
<form id="feedback-form">
<!-- other inputs -->
</form>
<textarea name="message" form="feedback-form">
</textarea>
autocapitalize Jump to this section
Controls automatic capitalization on mobile devices.
Values: off, none, on, sentences, words, characters
<textarea autocapitalize="sentences">
First letter of sentences capitalized
</textarea>
<textarea autocapitalize="off">
no automatic capitalization
</textarea>
autocorrect Jump to this section
Controls automatic text correction on iOS devices. Non-standard but widely used.
<textarea autocorrect="on">
Auto-correction enabled
</textarea>
<textarea autocorrect="off">
No auto-correction
</textarea>
dirname Jump to this section
Submits the text directionality (ltr or rtl) along with the content. The attribute value becomes the name for the direction data.
<textarea name="comment" dirname="comment.dir">
</textarea>
<!-- Submits: comment=text content&comment.dir=ltr -->
aria-label Jump to this section
Provides an accessible label for the textarea when a visible <label> element isn't present.
<textarea aria-label="Enter your feedback">
</textarea>
aria-describedby Jump to this section
References additional descriptive text by its id to provide context or instructions.
<textarea id="bio" aria-describedby="bio-help">
</textarea>
<small id="bio-help">Write a brief description about yourself</small>
aria-invalid Jump to this section
Indicates whether the textarea has a validation error. Should be updated dynamically with JavaScript.
<textarea aria-invalid="true" aria-describedby="error-msg">
</textarea>
<span id="error-msg">This field contains errors</span>
aria-required Jump to this section
Indicates that the field is required. Use in addition to the required attribute for better accessibility.
<textarea required aria-required="true">
</textarea>
data-* attributes Jump to this section
Custom attributes for storing application-specific data on the textarea element.
<textarea data-max-words="500" data-type="review">
</textarea>
<script>
const textarea = document.querySelector('textarea');
const maxWords = textarea.dataset.maxWords; // "500"
const type = textarea.dataset.type; // "review"
</script>
id Jump to this section
Unique identifier for the textarea. Essential for linking with <label> elements and JavaScript selection.
<label for="message">Message:</label>
<textarea id="message" name="message">
</textarea>
class Jump to this section
Assigns CSS class names for styling or JavaScript selection.
<textarea class="form-control textarea-lg">
</textarea>
title Jump to this section
Provides a tooltip on hover. Not accessible to keyboard-only users or mobile, so don't rely on it for critical information.
<textarea title="Enter detailed feedback here">
</textarea>
tabindex Jump to this section
Controls keyboard focus order. Textarea elements are focusable by default, so rarely needed.
<textarea tabindex="1">
</textarea>
Styling basics Jump to this section
Textarea elements are highly customizable with CSS. Control appearance, resize behavior, and visual states to match your design system.
Basic textarea Jump to this section
.textarea-basic {
padding: 10px 14px;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 15px;
width: 100%;
font-family: inherit;
line-height: 1.5;
}
Resize control Jump to this section
Control how users can resize the textarea using the resize property.
/* Vertical only (most common) */
.textarea-vertical {
resize: vertical;
}
/* Horizontal only */
.textarea-horizontal {
resize: horizontal;
}
/* Both directions */
.textarea-both {
resize: both;
}
/* No resize */
.textarea-fixed {
resize: none;
}
Filled textarea (material style) Jump to this section
.textarea-filled {
padding: 12px 14px;
background: #f3f4f6;
border: none;
border-bottom: 2px solid #9ca3af;
border-radius: 6px 6px 0 0;
font-size: 15px;
resize: vertical;
}
Underline textarea Jump to this section
.textarea-underline {
padding: 8px 0;
background: transparent;
border: none;
border-bottom: 1px solid #d1d5db;
font-size: 15px;
resize: none;
}
Bordered with shadow Jump to this section
.textarea-shadow {
padding: 12px 14px;
border: 1px solid #e5e7eb;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
resize: vertical;
}
Size variants Jump to this section
.textarea-small {
padding: 6px 10px;
font-size: 13px;
border-radius: 4px;
}
.textarea-medium {
padding: 10px 14px;
font-size: 15px;
border-radius: 6px;
}
.textarea-large {
padding: 14px 18px;
font-size: 17px;
border-radius: 8px;
}
Disabled state Jump to this section
.textarea-basic:disabled {
background: #f3f4f6;
border-color: #e5e7eb;
color: #9ca3af;
cursor: not-allowed;
}
Readonly state Jump to this section
.textarea-basic:read-only {
background: #fafafa;
border-color: #e5e7eb;
cursor: default;
}
Error state Jump to this section
.textarea-error {
border: 2px solid #dc2626;
background: #fef2f2;
}
.textarea-error:focus {
outline: 2px solid #fca5a5;
outline-offset: 2px;
}
Success state Jump to this section
.textarea-success {
border: 2px solid #16a34a;
background: #f0fdf4;
}
Code editor style Jump to this section
.textarea-code {
padding: 12px;
background: #1e1e1e;
color: #d4d4d4;
border: 1px solid #3e3e3e;
border-radius: 6px;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.6;
}
Colored textarea Jump to this section
.textarea-colored {
padding: 12px 14px;
background: #3b82f6;
color: white;
border: none;
border-radius: 8px;
}
.textarea-colored::placeholder {
color: rgba(255, 255, 255, 0.7);
}
Focus states Jump to this section
Always provide clear focus indicators for accessibility. Use :focus pseudo-class.
Blue outline on focus Jump to this section
.textarea-basic {
border: 1px solid #d1d5db;
transition: all 0.2s;
}
.textarea-basic:focus {
border-color: #3b82f6;
outline: 2px solid #93c5fd;
outline-offset: 2px;
}
Glow effect on focus Jump to this section
.textarea-glow {
border: 1px solid #d1d5db;
transition: all 0.2s;
}
.textarea-glow:focus {
border-color: #8b5cf6;
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
outline: none;
}
Auto-expanding textarea Jump to this section
Textarea that grows with content (requires JavaScript).
.textarea-auto {
resize: none;
overflow: hidden;
min-height: 60px;
}
// See JavaScript tab for implementation
With character counter Jump to this section
.counter-wrapper {
position: relative;
}
.char-counter {
position: absolute;
bottom: 8px;
right: 12px;
font-size: 12px;
color: #6b7280;
background: white;
padding: 2px 6px;
border-radius: 4px;
}
JavaScript patterns Jump to this section
Textareas are commonly used for comments, messages, and long-form input. Use event listeners to enhance functionality and provide real-time feedback.
Getting and setting value Jump to this section
Access the current text content using the value property.
const textarea = document.getElementById('js-value');
// Get the current value
const text = textarea.value;
// Set new value
textarea.value = 'New text content';
// Append text
textarea.value += '\nAdditional line';
// Clear textarea
textarea.value = '';
Character counter Jump to this section
Display remaining or used characters in real-time.
const textarea = document.getElementById('js-counter');
const counter = document.getElementById('js-counter-display');
const maxLength = textarea.maxLength;
function updateCounter() {
const length = textarea.value.length;
counter.textContent = `${length} / ${maxLength}`;
// Change color when near limit
if (length > maxLength * 0.9) {
counter.style.color = '#dc2626';
} else if (length > maxLength * 0.7) {
counter.style.color = '#f59e0b';
} else {
counter.style.color = '#6b7280';
}
}
textarea.addEventListener('input', updateCounter);
updateCounter(); // Initial count
Auto-expanding textarea Jump to this section
Automatically adjust height based on content.
const textarea = document.getElementById('js-autoexpand');
function autoExpand() {
// Reset height to auto to get correct scrollHeight
textarea.style.height = 'auto';
// Set height to scrollHeight
textarea.style.height = textarea.scrollHeight + 'px';
}
textarea.addEventListener('input', autoExpand);
// Call on load if there's initial content
autoExpand();
Word counter Jump to this section
Count words instead of characters.
const textarea = document.getElementById('js-wordcount');
const wordDisplay = document.getElementById('word-count');
const charDisplay = document.getElementById('char-count');
function countWords() {
const text = textarea.value.trim();
// Count words (split by whitespace)
const words = text === '' ? 0 :
text.split(/\s+/).length;
// Count characters (excluding spaces)
const chars = text.replace(/\s/g, '').length;
wordDisplay.textContent = words;
charDisplay.textContent = chars;
}
textarea.addEventListener('input', countWords);
Save draft automatically Jump to this section
Auto-save content to localStorage as the user types.
const textarea = document.getElementById('js-autosave');
const status = document.getElementById('js-autosave-status');
const storageKey = 'draft-content';
let saveTimeout;
// Restore saved draft
const saved = localStorage.getItem(storageKey);
if (saved) {
textarea.value = saved;
status.textContent = 'Draft restored';
}
// Auto-save with debounce
textarea.addEventListener('input', () => {
clearTimeout(saveTimeout);
status.textContent = 'Typing...';
saveTimeout = setTimeout(() => {
localStorage.setItem(storageKey, textarea.value);
status.textContent = 'Draft saved ✓';
setTimeout(() => {
status.textContent = '';
}, 2000);
}, 1000); // Save 1 second after typing stops
});
Insert text at cursor Jump to this section
Insert text at the current cursor position.
function insertAtCursor(textareaId, text) {
const textarea = document.getElementById(textareaId);
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const value = textarea.value;
// Insert text at cursor
textarea.value = value.substring(0, start) +
text +
value.substring(end);
// Move cursor after inserted text
const newPos = start + text.length;
textarea.setSelectionRange(newPos, newPos);
textarea.focus();
}
Markdown preview toggle Jump to this section
Toggle between edit and preview modes.
const textarea = document.getElementById('js-markdown');
const preview = document.getElementById('js-preview');
function switchMode(mode) {
const editTab = document.getElementById('edit-tab');
const previewTab = document.getElementById('preview-tab');
if (mode === 'edit') {
textarea.style.display = 'block';
preview.style.display = 'none';
editTab.style.background = 'white';
previewTab.style.background = 'transparent';
} else {
// Simple markdown rendering
const html = textarea.value
.replace(/\*\*(.*?)\*\*/g, '$1')
.replace(/\*(.*?)\*/g, '$1')
.replace(/^- (.+)$/gm, '$1 ')
.replace(/(.*<\/li>)/s, '$&
');
preview.innerHTML = html;
textarea.style.display = 'none';
preview.style.display = 'block';
editTab.style.background = 'transparent';
previewTab.style.background = 'white';
}
}
Tab key support Jump to this section
Allow tab key to insert tabs instead of moving focus.
const textarea = document.getElementById('js-tab');
textarea.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
e.preventDefault(); // Prevent focus change
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const value = textarea.value;
// Insert tab character
textarea.value = value.substring(0, start) +
'\t' +
value.substring(end);
// Move cursor after tab
textarea.selectionStart = start + 1;
textarea.selectionEnd = start + 1;
}
});
Validate on submit Jump to this section
Check content before allowing form submission.
const form = document.getElementById('js-validate-form');
const textarea = document.getElementById('js-validate');
const error = document.getElementById('js-validate-error');
form.addEventListener('submit', (e) => {
e.preventDefault();
const text = textarea.value.trim();
if (text.length === 0) {
error.textContent = 'This field is required';
textarea.style.borderColor = '#dc2626';
return;
}
if (text.length < 10) {
error.textContent = 'Please enter at least 10 characters';
textarea.style.borderColor = '#dc2626';
return;
}
error.textContent = '';
textarea.style.borderColor = '#d1d5db';
alert('Form submitted!');
});
textarea.addEventListener('input', () => {
error.textContent = '';
textarea.style.borderColor = '#d1d5db';
});
Copy to clipboard Jump to this section
Add a button to copy textarea content.
function copyToClipboard() {
const textarea = document.getElementById('js-copy');
const button = document.getElementById('copy-btn');
// Select and copy text
textarea.select();
document.execCommand('copy');
// Or use modern API
// navigator.clipboard.writeText(textarea.value);
// Update button
const originalText = button.textContent;
button.textContent = 'Copied!';
button.style.background = '#16a34a';
setTimeout(() => {
button.textContent = originalText;
button.style.background = '#3b82f6';
}, 2000);
}
Disable resize on mobile Jump to this section
Prevent resize handle on mobile devices while allowing it on desktop.
textarea {
resize: vertical;
}
/* Disable resize on mobile */
@media (max-width: 768px) {
textarea {
resize: none;
}
}
Limit lines of input Jump to this section
Prevent users from entering more than a certain number of lines.
const textarea = document.getElementById('js-maxlines');
const maxLines = 3;
textarea.addEventListener('input', () => {
const lines = textarea.value.split('\n');
if (lines.length > maxLines) {
// Remove extra lines
textarea.value = lines.slice(0, maxLines).join('\n');
}
});
textarea.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
const lines = textarea.value.split('\n');
if (lines.length >= maxLines) {
e.preventDefault();
}
}
});
Mention autocomplete Jump to this section
Basic @ mention functionality.
const textarea = document.getElementById('js-mention');
const mentionList = document.getElementById('mention-list');
const users = ['Alice', 'Bob', 'Charlie', 'Diana'];
textarea.addEventListener('input', (e) => {
const value = textarea.value;
const cursorPos = textarea.selectionStart;
const textBeforeCursor = value.substring(0, cursorPos);
const match = textBeforeCursor.match(/@(\w*)$/);
if (match) {
const query = match[1].toLowerCase();
const matches = users.filter(u =>
u.toLowerCase().startsWith(query)
);
if (matches.length > 0) {
mentionList.innerHTML = matches.map(user =>
`${user}`
).join('');
mentionList.style.display = 'block';
} else {
mentionList.style.display = 'none';
}
} else {
mentionList.style.display = 'none';
}
});
function insertMention(name) {
const value = textarea.value;
const cursorPos = textarea.selectionStart;
const textBeforeCursor = value.substring(0, cursorPos);
const textAfterCursor = value.substring(cursorPos);
const newTextBefore = textBeforeCursor.replace(/@\w*$/, `@${name} `);
textarea.value = newTextBefore + textAfterCursor;
textarea.setSelectionRange(newTextBefore.length, newTextBefore.length);
mentionList.style.display = 'none';
textarea.focus();
}
