Los estados vacíos parecen una parte menor de una app, pero afectan bastante la sensación de calidad. Cuando una tabla, una bandeja o una búsqueda no tiene resultados, ese momento le dice a la persona si la interfaz entiende su contexto o solo muestra un hueco elegante. En equipos frontend he visto que este detalle se deja para el final, y luego cuesta mucho explicar por qué una vista se siente rara aunque todo "funcione".
Además, ese primer vacío aparece muy pronto: al abrir una cuenta nueva, al limpiar filtros o al empezar un flujo interno. Incluso en herramientas de soporte, donde alguien anota cosas como temp org mail para pruebas, el problema no es solo no tener datos, sino no saber qué hacer despues.
Por qué los estados vacíos suelen confundir más de lo que ayudan
Muchos empty states fallan por tres razones:
- describen el problema con una frase muy genérica
- no explican la siguiente acción posible
- cambian tanto el layout que parece otra pantalla distinta
Eso rompe continuidad. La persona venía leyendo una lista y de pronto encuentra una ilustración, un bloque centrado y un botón que quizá ni aplica. No es dramático, pero sí desgasta. Nielsen Norman Group lleva años insistiendo en que los mensajes de interfaz deben reducir incertidumbre y guiar la acción, no solo llenar espacio (https://www.nngroup.com/articles/error-message-guidelines/).
También me gusta mirar este tema desde el mismo criterio con el que intentamos aislar flujos automatizados sin perder trazabilidad: cuando el estado es ambiguo, el usuario termina adivinando. En frontend, adivinar casi siempre significa fricción.
Qué información necesita ver una persona en ese momento
Un buen estado vacío no necesita ser largo. Normalmente basta con responder cuatro preguntas:
- Qué está vacío.
- Por qué puede estar vacío.
- Qué acción inmediata conviene hacer.
- Qué pasa si no hago nada ahora.
Por ejemplo, "No hay resultados" sirve poco. En cambio, "Todavía no tienes campañas activas. Crea la primera o cambia el rango de fechas" ya orienta mucho mejor. Ese pequeño cambio reduce carga cognitiva, y se nota aunque el componente sea sencillo.
En productos con métricas o mensajería interna, también conviene distinguir entre "aún no existe contenido" y "el filtro actual no muestra contenido". Parece un matiz pequeño, pero evita tickets innecesarios. Ese mismo tipo de claridad aparece cuando alguien necesita probar upgrades por email sin ruido: una interfaz útil separa causas distintas en vez de meterlas en el mismo cajón.
Un patrón simple en React para no rehacer la pantalla entera
Mi enfoque preferido es mantener la estructura base y cambiar solo la capa de contenido. Así el usuario conserva referencias visuales y el layout no "salta" tanto. Algo así:
type EmptyStateProps = {
title: string;
message: string;
actionLabel?: string;
onAction?: () => void;
};
function EmptyState({ title, message, actionLabel, onAction }: EmptyStateProps) {
return (
<section className="empty-state" aria-live="polite">
<h2>{title}</h2>
<p>{message}</p>
{actionLabel && onAction ? (
<button type="button" onClick={onAction}>
{actionLabel}
</button>
) : null}
</section>
);
}
Y luego, dentro de la vista principal:
if (isLoading) return <SkeletonList />;
if (items.length === 0) {
return (
<EmptyState
title="No hay elementos todavía"
message="Crea el primero o ajusta los filtros para ver resultados."
actionLabel="Crear elemento"
onAction={() => openCreateModal()}
/>
);
}
return <ItemsTable items={items} />;
Lo importante no es el componente en sí, sino el contrato. Un estado vacío debería recibir intención, no solo texto suelto. Si el mensaje depende del filtro, del permiso o del onboarding, conviene modelarlo desde datos y no resolverlo con condicionales desperdigados. Suena obvio, pero muchas vistas crecen medio sin querer y acaban dificiles de mantener.
Detalles de CSS y accesibilidad que cambian la percepción
En CSS, casi siempre prefiero evitar el centrado total de pantalla salvo que sea una pantalla inicial. Cuando el empty state vive dentro de una tabla, una lista o un panel, funciona mejor respetar contenedores, spacing y anchuras del resto de la UI.
.empty-state {
display: grid;
gap: 0.75rem;
padding: 1.25rem;
border: 1px solid var(--border-subtle);
border-radius: 16px;
background: linear-gradient(180deg, #fffdf8, #fffaf0);
}
.empty-state p {
max-width: 48ch;
color: var(--text-muted);
}
Hay tres comprobaciones que me ayudan bastante:
- que el título explique contexto real y no marketing
- que el botón tenga una consecuencia clara
- que
aria-livesolo se use cuando el contenido cambia tras una acción del usuario
Sobre rendimiento, no hace falta obsesionarse, pero sí conviene evitar estados vacíos enormes con ilustraciones pesadas o librerías extra. El Web Almanac muestra de forma repetida que el peso de JavaScript sigue siendo uno de los factores que más penalizan la experiencia en la web (https://almanac.httparchive.org/en/2024/javascript). A veces el estado más rapido y más claro es también el más humilde.
Q&A rápida
¿Conviene esconder filtros cuando no hay datos?
Casi nunca. Si el vacío viene de los filtros, esconderlos empeora el problema. Mejor mantenerlos visibles y dejar claro qué está limitando los resultados.
¿Hace falta una ilustración?
No siempre. Si aporta contexto, bien. Si solo ocupa espacio, sobra. En bastantes productos B2B una buena jerarquía tipográfica resuelve más que un dibujo bonito.
¿El estado vacío debe ser igual en desktop y móvil?
La intención sí, pero el layout no necesariamente. En móvil me funciona mejor reducir texto, apilar acciones y evitar bloques demasiado altos. Si no, la acción principal queda muy abajo y la pantalla se siente torpe, casi incomoda.
Checklist antes de publicar
- El mensaje distingue entre "sin datos todavía" y "sin resultados por filtro".
- La acción principal es específica y visible.
- El layout conserva la estructura de la vista anterior.
- El componente no añade peso innecesario ni dependencias raras.
- La accesibilidad está pensada para cambios reales de estado, no por costumbre.
Cuando un estado vacío está bien resuelto, la interfaz parece más tranquila, más clara y bastante más confiable. No suele ganar aplausos en la demo, pero en uso diario se nota un monton.

Top comments (0)