Etiqueta textarea en HTML: texto multilínea y límites
La etiqueta <textarea> en HTML crea un campo para introducir y editar texto de varias líneas. Úsala para un mensaje de contacto, una reseña o una descripción que pueda necesitar párrafos. A diferencia de <input type="text">, permite insertar saltos de línea con Enter; aumentar la altura de un <input> con CSS no le da esa capacidad.
<label for="mensaje">Mensaje</label>
<textarea id="mensaje" name="mensaje" rows="4" maxlength="500"></textarea>
Verás un campo vacío, etiquetado «Mensaje», con espacio inicial para cuatro líneas y un límite de 500 unidades de texto. Prueba el fragmento dentro del <body> de un documento HTML con codificación UTF-8: pulsa la etiqueta, escribe y usa Enter. No necesita JavaScript. El campo aislado no envía nada; name="mensaje" identifica su dato cuando se asocia a un formulario.
Sintaxis, contenido y texto inicial
Escribe apertura y cierre: <textarea>texto inicial</textarea>. Ninguna etiqueta puede omitirse y <textarea /> no cierra el elemento en HTML. Se permite donde se admite contenido de frase, por ejemplo dentro de un formulario o un párrafo. Su contenido es texto, según el modelo de contenido de WHATWG.
¿Cómo pongo texto inicial? Escríbelo entre las etiquetas:
<label for="consulta">Consulta</label>
<textarea id="consulta" name="consulta" rows="3">Quisiera reservar una visita.
¿Qué horarios tienen?</textarea>
El campo empieza con esas dos líneas, editables. La indentación también cuenta como texto: evita añadir espacios para alinear su contenido con el resto del código. El analizador HTML elimina un salto de línea inmediatamente después de la apertura; otros saltos y espacios se conservan. La sintaxis HTML del estándar describe esta excepción.
Para mostrar literalmente <strong> o &, escribe <strong> o &. El contenido no se convierte en negrita ni ejecuta etiquetas. Escapar < también evita que una cadena como </textarea> cierre el campo antes de tiempo.
Valor inicial, value y restablecimiento
¿Por qué no funciona value como atributo? <textarea value="Hola"></textarea> es un uso incorrecto: el elemento define su valor inicial mediante el contenido textual, y en ese ejemplo sigue vacío. placeholder="Hola" tampoco establece un valor; solo muestra una sugerencia cuando el campo está vacío.
JavaScript utiliza propiedades del elemento. Añade este script después del campo consulta:
<script>
const campo = document.getElementById("consulta");
console.log(campo.value); // Texto actual, incluidas las ediciones.
console.log(campo.defaultValue); // Texto inicial entre las etiquetas.
campo.value = "Otra consulta"; // Cambia el texto actual.
</script>
Ejecuta este fragmento después de crear el campo consulta. .value lee o cambia el valor actual; .defaultValue refleja el contenido textual que sirve de valor predeterminado. Asignar .value no cambia ese valor predeterminado. Si quieres cambiarlo también, modifica .defaultValue.
Dentro de un formulario, un botón type="reset" o formulario.reset() devuelve el campo al valor predeterminado, no necesariamente a una cadena vacía. Si lo dejaste vacío en el HTML, se vaciará; si escribiste una consulta inicial, la recuperará.
Atributos, longitud y ajuste de líneas
Estos atributos propios de controles de formulario cumplen funciones distintas:
name: nombre del dato, comoname="mensaje". Sin él, el texto no se incluye en los datos del formulario;idno lo sustituye.rowsycols: líneas visibles y anchura orientativa en caracteres. Admiten enteros positivos; los valores predeterminados son 2 y 20. No limitan cuántas líneas puedes escribir ni fijan un tamaño CSS inmutable.maxlengthyminlength: límites máximo y mínimo, enteros no negativos. El mínimo no obliga a rellenar un campo opcional; añaderequiredpara exigir contenido. Si usas ambos, el mínimo no puede superar el máximo.required: impide validar un valor vacío. No significa que el mensaje tenga contenido útil: unos espacios pueden cumplir el requisito.placeholder: pista breve; no reemplaza la etiqueta, la ayuda ni el texto inicial.readonly: permite enfocar, seleccionar y copiar, pero impide editar. Su valor puede incluirse en el formulario; el control queda fuera de la validación de restricciones.disabled: deshabilita la interacción, normalmente lo excluye del recorrido con Tab y excluye su dato del envío. Consulta las referencias de solo lectura y deshabilitación.wrap:soft, predeterminado, ajusta la presentación sin añadir saltos automáticos al dato; conserva los escritos con Enter.hardañade saltos al dato recogido para el envío según la anchura; acompáñalo decols. No limita la longitud total del mensaje.autocomplete: sugiere el autocompletado conon,offo tokens adecuados al dato. Sin él, hereda el estado del formulario; el navegador decide cómo aplicar la sugerencia. Consulta los valores de autocompletado.form: asocia el campo al<form>cuyoidindica, aunque esté fuera:form="contacto". Sin él, se asocia al formulario antecesor, si existe; también puede existir sin formulario.
required, readonly y disabled son booleanos: su presencia los activa. disabled="false" sigue deshabilitando; retira el atributo para habilitar.
Los límites maxlength y minlength miden unidades UTF-16, igual que .value.length: ciertos emojis ocupan dos unidades y un símbolo visible puede combinar varias. Los saltos en .value se normalizan a \n y cuentan una unidad. No prometas «500 símbolos visibles». El navegador normalmente evita superar el máximo al escribir, pero asignar texto por JavaScript no lo recorta automáticamente; la validación de longitud depende de ediciones del usuario. Un servidor real debe comprobar sus propios límites.
id, class y lang son atributos globales; aria-describedby asocia ayuda accesible. width, height y resize son propiedades CSS; .value y .defaultValue son propiedades DOM. Cambiar la apariencia conserva la función multilínea.
Ejemplo práctico: contacto con contador y errores
Guarda este documento completo como contacto.html en UTF-8 y ábrelo en el navegador. Incluye su CSS y JavaScript; no necesita archivos adicionales ni servidor. La demostración no envía mensajes ni guarda datos. El botón de comprobación empieza deshabilitado y solo se habilita al ejecutar el script. Sin JavaScript permanecen la etiqueta, la ayuda, el campo editable y el restablecimiento.
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Consulta al taller</title>
<style>
body { font: 1rem/1.5 system-ui; margin: 1rem; }
#contacto { max-width: 36rem; }
#contacto label { display: block; font-weight: bold; }
#contacto textarea {
box-sizing: border-box;
width: 100%;
min-height: 8rem;
font: inherit;
resize: vertical;
}
#contacto :focus-visible { outline: 3px solid #174ea6; outline-offset: 3px; }
#contacto-error { color: #9b1c1c; }
</style>
</head>
<body>
<h1>Consulta al taller</h1>
<p>Prueba local: no se enviará ningún mensaje.</p>
<form id="contacto">
<label for="contacto-mensaje">Mensaje (obligatorio)</label>
<p id="contacto-ayuda">Escribe entre 20 y 500 unidades de texto.
Puedes usar varias líneas; algunos emojis cuentan como dos unidades.</p>
<textarea id="contacto-mensaje" name="mensaje" rows="5" cols="40"
minlength="20" maxlength="500" required wrap="soft"
aria-describedby="contacto-ayuda contacto-error"></textarea>
<p id="contacto-contador" hidden>Longitud actual: <span>0</span> de 500.</p>
<p id="contacto-error" hidden></p>
<button id="contacto-comprobar" type="button" disabled>Comprobar mensaje</button>
<button type="reset">Restablecer</button>
<p id="contacto-estado" role="status"></p>
</form>
<noscript><p>El contador y la comprobación local necesitan JavaScript.
Puedes escribir y restablecer el texto; no se envía nada.</p></noscript>
<script>
const formulario = document.getElementById("contacto");
const mensaje = document.getElementById("contacto-mensaje");
const contador = document.getElementById("contacto-contador");
const error = document.getElementById("contacto-error");
const estado = document.getElementById("contacto-estado");
const comprobar = document.getElementById("contacto-comprobar");
function actualizarContador() {
contador.querySelector("span").textContent = mensaje.value.length;
}
function limpiarAvisos() {
error.hidden = true;
error.textContent = "";
mensaje.removeAttribute("aria-invalid");
estado.textContent = "";
}
formulario.addEventListener("submit", (evento) => evento.preventDefault());
mensaje.addEventListener("input", () => {
actualizarContador();
limpiarAvisos();
});
comprobar.addEventListener("click", () => {
limpiarAvisos();
if (!mensaje.checkValidity()) {
error.textContent = mensaje.validity.valueMissing
? "Error: escribe un mensaje."
: "Error: el mensaje debe tener entre 20 y 500 unidades de texto.";
error.hidden = false;
mensaje.setAttribute("aria-invalid", "true");
mensaje.focus();
return;
}
estado.textContent = "Mensaje válido para esta prueba. No se envió nada.";
});
formulario.addEventListener("reset", () => {
limpiarAvisos();
// El evento ocurre antes de restaurar el valor del campo.
setTimeout(actualizarContador, 0);
});
actualizarContador();
contador.hidden = false;
comprobar.disabled = false;
</script>
</body>
</html>
Prueba estos pasos:
- Con Tab llega al campo, escribe
Necesito una revisión.y pulsa Enter para añadir otra línea. Verás el contador actualizarse y podrás ampliar el campo verticalmente con su tirador, si el navegador lo ofrece. - Llega a «Comprobar mensaje» con Tab y actívalo con Enter o Espacio. Con ese texto verás la confirmación local. Enter dentro del campo escribe un salto; no comprueba ni envía el mensaje.
- Vacía el campo o escribe
Holay comprueba otra vez. Verás un error textual, asociado al campo mediantearia-describedby, y el foco volverá al campo marcado conaria-invalid. Al editar se limpia el aviso anterior. - Pulsa «Restablecer»: recuperarás el valor inicial vacío y el contador volverá a cero. Añadir texto entre las etiquetas cambia también lo que recupera este botón.
- Deshabilita JavaScript y recarga. Podrás escribir, leer la ayuda y restablecer; el contador estará oculto y la comprobación deshabilitada.
El contador es una ayuda visual opcional: no se anuncia como región viva en cada pulsación. La confirmación usa role="status"; los errores se comunican con texto y asociación, además del color. El CSS permite ampliar la altura; evita resize: none cuando el lector pueda necesitar más espacio.
Errores frecuentes y elección de etiquetas
Si no aparece el texto inicial, comprueba primero que esté entre apertura y cierre, que no uses un atributo value y que ningún script sobrescriba .value. Si falta el dato al recoger el formulario, revisa name, disabled y la asociación form. Si el campo tiene espacios al cargar, revisa su indentación.
Retirar <textarea> dejando su contenido convierte el mensaje en texto de la página: deja de ser editable y no aporta un dato al formulario. Sustituirlo por <input type="text"> crea un control de una sola línea; sustituirlo por un <div> no crea un control de formulario nativo.
- Usa
<input>para una línea o un tipo específico, como correo o fecha. - Usa
<label>para nombrar el campo de forma visible y asociada; el placeholder desaparece al escribir. - Usa
<form>para reunir controles y configurar un envío real con un destino que procese los datos. - Usa
<output>para un resultado calculado, como un total; no permite editar un mensaje. Su semántica de resultado puede anunciar actualizaciones, por lo que este contador utiliza un<span>sencillo.
Compatibilidad y fuentes
<textarea> está disponible en los navegadores actuales. La referencia y compatibilidad de MDN distingue el elemento y sus atributos. La apariencia, el tirador de redimensionado y el autocompletado pueden variar entre navegador y dispositivo; no dependas de un aspecto idéntico. Los ejemplos usan el control nativo, límites habituales y propiedades DOM establecidas, sin una función experimental.
Consulta el HTML Living Standard para <textarea> para la definición normativa y wrap, y las referencias enlazadas en cada sección para valores, validación y restablecimiento. Fuentes revisadas el 4 de octubre de 2026.