Formularios
Los formularios son la única forma que tiene un usuario de enviar datos a una web: registrarse, iniciar sesión, buscar, comprar, comentar, contactar... Sin formularios, la web sería solo lectura. Esta es una de las lecciones más importantes del curso.
La etiqueta <form>
Todo formulario empieza con <form>. Esta etiqueta agrupa todos los campos y define a dónde se envían los datos:
<form action="/enviar-contacto" method="POST">
<!-- Campos del formulario van aquí -->
</form>
action: la URL del servidor que procesará los datos.method: cómo se envían.GETpone los datos en la URL (búsquedas),POSTlos envía en el cuerpo de la petición (datos sensibles, formularios largos).
Inputs: los campos del formulario
La etiqueta <input> es la más versátil. Su atributo type define qué tipo de campo muestra:
<form action="/registro" method="POST">
<label for="nombre">Nombre completo</label>
<input type="text" id="nombre" name="nombre" required>
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
<label for="password">Contraseña</label>
<input type="password" id="password" name="password"
minlength="8" required>
<label for="nacimiento">Fecha de nacimiento</label>
<input type="date" id="nacimiento" name="nacimiento">
<button type="submit">Crear cuenta</button>
</form>
Tipos de input que debes conocer
| Tipo | Qué muestra | Ejemplo de uso |
|---|---|---|
text |
Campo de texto libre | Nombre, ciudad, empresa |
email |
Campo que valida formato de email | [email protected] |
password |
Oculta los caracteres | Contraseña, PIN |
number |
Solo números, con flechas ↑↓ | Edad, cantidad, precio |
tel |
Teclado numérico en móvil | Teléfono |
url |
Valida formato de URL | Sitio web, portfolio |
date |
Selector de fecha nativo | Fecha de nacimiento, reserva |
time |
Selector de hora | Hora de la cita |
color |
Selector de color | Color favorito, tema |
range |
Slider (barra deslizante) | Volumen, brillo, precio máx. |
file |
Selector de archivos | Subir avatar, documentos |
search |
Campo de búsqueda (con X para limpiar) | Buscador del sitio |
checkbox |
Casilla de verificación | Aceptar términos, preferencias |
radio |
Opción única entre varias | Plan, género, método de pago |
hidden |
Campo invisible (datos internos) | ID del producto, token CSRF |
Usar el
typecorrecto no es solo estético. En móvil,type="email"muestra un teclado con@,type="tel"muestra un teclado numérico. Los usuarios te lo agradecen.
Labels: accesibilidad obligatoria
Cada campo necesita un <label>. Sin label, los usuarios con lectores de pantalla no saben qué dato pide cada campo:
<!-- Método 1: label con for + input con id (recomendado) -->
<label for="ciudad">Ciudad</label>
<input type="text" id="ciudad" name="ciudad">
<!-- Método 2: input dentro del label (también válido) -->
<label>
Ciudad
<input type="text" name="ciudad">
</label>
El atributo for del label debe coincidir con el id del input. Esto tiene un bonus: al hacer clic en el label, el navegador enfoca el input. En checkboxes y radios esto es especialmente útil porque amplía la zona clicable.
Textarea, select y datalist
<form action="/feedback" method="POST">
<!-- Área de texto para textos largos -->
<label for="comentario">Tu opinión</label>
<textarea id="comentario" name="comentario" rows="5"
placeholder="Cuéntanos qué podemos mejorar..."></textarea>
<!-- Select para elegir una opción de una lista -->
<label for="categoria">Categoría</label>
<select id="categoria" name="categoria">
<option value="">Selecciona una categoría</option>
<option value="bug">Bug o error</option>
<option value="feature">Sugerencia de funcionalidad</option>
<option value="ui">Diseño / interfaz</option>
<option value="rendimiento">Rendimiento</option>
</select>
<!-- Datalist: input con sugerencias (autocomplete libre) -->
<label for="navegador">Navegador</label>
<input type="text" id="navegador" name="navegador" list="navegadores">
<datalist id="navegadores">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
<option value="Edge">
<option value="Arc">
<option value="Brave">
</datalist>
<button type="submit">Enviar feedback</button>
</form>
La diferencia entre <select> y <datalist>: con select estás limitado a las opciones que hay. Con datalist puedes escribir lo que quieras además de elegir las sugerencias.
Validación nativa de HTML
No necesitas JavaScript para validar formularios básicos. HTML tiene validación incorporada:
<form action="/pedido" method="POST">
<!-- Campo obligatorio -->
<label for="producto">Producto</label>
<input type="text" id="producto" name="producto" required>
<!-- Rango de valores -->
<label for="cantidad">Cantidad (1-99)</label>
<input type="number" id="cantidad" name="cantidad"
min="1" max="99" value="1" required>
<!-- Patrón regex -->
<label for="codigo">Código postal (5 dígitos)</label>
<input type="text" id="codigo" name="codigo"
pattern="[0-9]{5}"
title="Introduce un código postal de 5 dígitos"
required>
<!-- Longitud mínima y máxima -->
<label for="usuario">Nombre de usuario (3-20 caracteres)</label>
<input type="text" id="usuario" name="usuario"
minlength="3" maxlength="20" required>
<button type="submit">Hacer pedido</button>
</form>
| Atributo | Qué valida |
|---|---|
required |
El campo no puede estar vacío |
min / max |
Valor mínimo/máximo (números, fechas) |
minlength / maxlength |
Longitud mínima/máxima del texto |
pattern |
Expresión regular personalizada |
type="email" |
Formato de email válido |
type="url" |
Formato de URL válido |
Los pseudo-selectores CSS :user-valid y :user-invalid permiten estilizar los campos según su estado de validación, pero solo después de que el usuario interactúe con ellos. Esto evita que el formulario aparezca "lleno de errores" antes de escribir nada. Los verás en la sección de CSS.
Checkboxes y radio buttons
<form action="/preferencias" method="POST">
<!-- Checkboxes: múltiples opciones -->
<fieldset>
<legend>Géneros de videojuegos que te gustan</legend>
<label><input type="checkbox" name="generos[]" value="rpg"> RPG</label>
<label><input type="checkbox" name="generos[]" value="fps"> FPS</label>
<label><input type="checkbox" name="generos[]" value="estrategia"> Estrategia</label>
<label><input type="checkbox" name="generos[]" value="plataformas"> Plataformas</label>
<label><input type="checkbox" name="generos[]" value="sandbox"> Sandbox</label>
</fieldset>
<!-- Radio buttons: solo una opción -->
<fieldset>
<legend>Plataforma principal</legend>
<label><input type="radio" name="plataforma" value="pc" checked> PC</label>
<label><input type="radio" name="plataforma" value="ps5"> PlayStation 5</label>
<label><input type="radio" name="plataforma" value="xbox"> Xbox Series X</label>
<label><input type="radio" name="plataforma" value="switch"> Nintendo Switch 2</label>
</fieldset>
<button type="submit">Guardar preferencias</button>
</form>
<fieldset> agrupa campos relacionados y <legend> le da un título al grupo. Esto es clave para accesibilidad: los lectores de pantalla anuncian "Géneros de videojuegos que te gustan" antes de cada checkbox del grupo.
Los radio buttons con el mismo name forman un grupo exclusivo: solo puedes seleccionar uno.
Ejemplo completo: formulario de reserva
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reservar mesa — Restaurante Umami</title>
</head>
<body>
<h1>Reserva tu mesa en Umami</h1>
<p>Cocina fusión japonesa-mediterránea en el centro de Madrid.</p>
<form action="/reservar" method="POST">
<label for="nombre">Nombre de la reserva</label>
<input type="text" id="nombre" name="nombre" required
autocomplete="name">
<label for="email">Email de confirmación</label>
<input type="email" id="email" name="email" required
autocomplete="email">
<label for="telefono">Teléfono</label>
<input type="tel" id="telefono" name="telefono" required
autocomplete="tel">
<label for="fecha">Fecha</label>
<input type="date" id="fecha" name="fecha" required
min="2026-03-15">
<label for="hora">Hora</label>
<input type="time" id="hora" name="hora" required
min="13:00" max="23:00" step="1800">
<label for="comensales">Número de comensales</label>
<input type="number" id="comensales" name="comensales"
min="1" max="12" value="2" required>
<fieldset>
<legend>Zona preferida</legend>
<label><input type="radio" name="zona" value="interior" checked> Interior</label>
<label><input type="radio" name="zona" value="terraza"> Terraza</label>
<label><input type="radio" name="zona" value="barra"> Barra</label>
</fieldset>
<label for="notas">Notas especiales</label>
<textarea id="notas" name="notas" rows="3"
placeholder="Alergias, celebraciones, silla para bebé..."></textarea>
<label>
<input type="checkbox" name="terminos" required>
Acepto la política de cancelación
</label>
<button type="submit">Confirmar reserva</button>
</form>
</body>
</html>
Fíjate en el atributo
autocomplete. Le dice al navegador qué tipo de dato es para que pueda autocompletar con los datos guardados del usuario. Mejora mucho la experiencia en móvil.
Crea un formulario de registro para una plataforma
Crea un archivo registro.html con un formulario de registro para una plataforma inventada (una app de recetas, un marketplace de segunda mano, una red social de mascotas...). Debe incluir:
- Campos de texto: nombre, email, contraseña
- Un
<select>para elegir el país - Checkboxes para intereses o preferencias
- Radio buttons para elegir un plan (free/premium)
- Un
<textarea>para "Cuéntanos sobre ti" - Validación nativa:
required,minlength,patterndonde tenga sentido - Todos los campos con
<label> - Al menos un
<fieldset>con<legend>
lightbulb Pistas
Usa autocomplete en los campos de nombre, email y teléfono. Para la contraseña, usa minlength="8" y autocomplete="new-password". El checkbox de "Acepto los términos" debe ser required.