Aprende un sistema sencillo para estados de carga, error y vacío coherentes entre web y móvil, de modo que la UI generada por IA sea coherente y necesite menos pulido al final.

Los estados de carga, error y vacío son las pantallas (o pequeños bloques de UI) que la gente ve cuando la app está esperando, algo falló o simplemente no hay nada que mostrar. Son normales: las redes son lentas, se niegan permisos y las cuentas nuevas empiezan con cero datos.
Se vuelven desordenados porque normalmente se añaden tarde y rápido. Los equipos construyen primero el camino feliz y luego parchean un spinner, un mensaje en rojo y un marcador de “sin elementos” dondequiera que la UI se rompa. Haz eso en docenas de pantallas y acabarás con montones de soluciones únicas.
La iteración rápida lo empeora. Cuando la UI se produce rápido (incluida la UI generada por IA), el diseño principal puede aparecer en minutos, pero estos estados son fáciles de saltarse. Cada nueva pantalla acaba con un estilo diferente de spinner, una redacción distinta (“Try again” vs “Retry”) y colocación del botón distinta. La velocidad que ganaste al principio se convierte en trabajo de pulido justo antes del lanzamiento.
Los estados descoordinados confunden a los usuarios y cuestan tiempo a los equipos. La gente no distingue si una lista vacía significa “sin resultados”, “no cargado aún” o “no tienes acceso”. QA tiene que probar una larga cola de pequeñas variaciones y se cuelan errores porque el comportamiento difiere entre web y móvil.
“Desordenado” a menudo se ve así:
El objetivo es simple: un enfoque compartido entre web y móvil. Si tu equipo genera funciones con rapidez (por ejemplo, usando una plataforma como Koder.ai), tener un patrón de estados compartido importa aún más porque cada nueva pantalla empieza coherente por defecto.
La mayoría de las apps repiten los mismos puntos críticos: listas, páginas de detalle, formularios y paneles. Aquí es donde se multiplican los spinners, banners y mensajes de “no hay nada”.
Empieza por nombrar y estandarizar cinco tipos de estado:
Dos casos especiales merecen reglas propias porque se comportan diferente:
Entre pantallas y plataformas, mantén la estructura consistente: dónde aparece el estado, el estilo del icono, el tono y las acciones por defecto (Retry, Refresh, Clear filters, Create). Lo que puede variar es el contexto: el nombre de la pantalla y una frase que use las palabras del usuario.
Ejemplo: si generas una lista web y una lista móvil para “Projects”, deberían compartir el mismo patrón de cero-resultados. La etiqueta de la acción puede aún coincidir con la plataforma (“Clear filters” vs “Reset”).
Si cada pantalla inventa su propio spinner, tarjeta de error y mensaje vacío, acabarás con una docena de versiones ligeramente diferentes. La solución más rápida es un pequeño “state kit” que cualquier función pueda usar.
Empieza con tres componentes reutilizables que funcionen en todas partes: Loading, Error y Empty. Mantenlos intencionadamente aburridos. Deben ser fáciles de reconocer y no competir con la UI principal.
Haz los componentes predecibles definiendo un conjunto pequeño de entradas:
Luego fija el aspecto. Decide una sola vez el espaciado, la tipografía, el tamaño del icono y el estilo del botón, y trátalo como una regla. Cuando el tamaño del icono y el tipo de botón se mantienen, los usuarios dejan de fijarse en la UI de estado y comienzan a confiar en ella.
Limita las variantes para que el kit no se convierta en un segundo sistema de diseño. Tres tamaños suelen cubrirlo: pequeño (inline), por defecto (sección) y página completa (bloqueante).
Si generas pantallas en Koder.ai, una instrucción simple como “use the app StateKit for loading/error/empty with default variant” evita la deriva. También reduce la limpieza de última hora entre React web y Flutter mobile.
El copy es parte del sistema, no decoración. Incluso cuando el layout es consistente, frases improvisadas hacen que las pantallas se sientan distintas.
Elige una voz compartida: corta, específica y calmada. Di qué pasó en términos simples y después dile al usuario qué hacer a continuación. La mayoría de las pantallas solo necesitan un título claro, una breve explicación y una acción obvia.
Algunos patrones de mensaje cubren la mayor parte de situaciones. Mantenlos cortos para que quepan en pantallas pequeñas:
Evita texto vago como “Something went wrong” por sí solo. Si realmente no sabes la causa, di lo que sí sabes y qué puede hacer el usuario ahora. “We couldn’t load your projects” es mejor que “Error.”
Aplica una regla: cada error y estado vacío ofrece un siguiente paso.
Esto importa aún más con UI generada por IA, donde las pantallas aparecen rápido. Las plantillas mantienen el copy consistente para que no tengas que reescribir docenas de mensajes únicos durante el pulido final.
Cuando las pantallas de estado sugieren acciones distintas de una página a otra, los usuarios dudan. Los equipos acaban ajustando botones y copy justo antes del lanzamiento.
Decide qué acción corresponde a cada estado y mantén la colocación y etiqueta coherentes. La mayoría de las pantallas deben tener una acción primaria. Si añades una segunda, debe apoyar la ruta principal, no competir con ella.
Mantén las acciones permitidas ajustadas:
Los botones aburridos son una ventaja. Hacen la UI familiar y ayudan a que las pantallas generadas permanezcan coherentes.
Muestra “Retry” solo cuando reintentar pueda funcionar (time-outs, red inestable, 5xx). Añade un pequeño debounce para que toques repetidos no saturen las peticiones, y cambia el botón a un estado de carga mientras se reintenta.
Tras fallos repetidos, mantiene el mismo botón primario y mejora la ayuda secundaria (por ejemplo, un consejo “Check connection” o “Try again later”). Evita introducir nuevos layouts solo porque algo falló dos veces.
Para los detalles de error, muestra una razón clara sobre la que el usuario pueda actuar (“Your session expired. Sign in again.”). Oculta detalles técnicos por defecto. Si los necesitas, ponlos tras una misma interacción de “Details” consistente entre plataformas.
Ejemplo: una lista de “Projects” falla al cargar en móvil. Ambas plataformas muestran la misma acción primaria “Retry”, la deshabilitan mientras reintentan y, tras dos fallos, añaden una pequeña pista de conexión en lugar de cambiar todo el layout del botón.
Trata la consistencia de estados como un pequeño cambio de producto, no como un rediseño. Ve incremental y facilita la adopción.
Empieza con un rápido inventario de lo que ya tienes. No busques la perfección. Captura las variaciones comunes: skeletons vs spinners, errores a página completa vs banners, pantallas de “no hay resultados” con distinto tono.
Un plan de despliegue práctico:
Una vez que los componentes existen, el verdadero ahorro de tiempo es un conjunto corto de reglas que elimina debates: cuándo un estado bloquea toda la página vs solo una tarjeta, y qué acciones deben estar presentes.
Mantén las reglas breves:
Si usas un generador de UI como Koder.ai, estas reglas se notan rápido. Puedes pedir “use the state kit components” y obtener pantallas que casen con tu sistema tanto en React web como en Flutter mobile con menos limpieza.
El trabajo de pulido de última hora suele ocurrir porque el manejo de estados se construyó como soluciones únicas. Una pantalla “funciona”, pero la experiencia se siente distinta cada vez que algo tarda, falla o no tiene datos.
Los skeletons ayudan, pero dejarlos en pantalla demasiado tiempo hace creer a la gente que la app se congeló. Una causa común es mostrar un skeleton completo en una llamada lenta sin señal de que algo avanza.
Pon un límite temporal: tras una breve demora, cambia a un mensaje más ligero de “Still loading…” o muestra el progreso cuando puedas.
Los equipos suelen escribir un mensaje nuevo cada vez, aunque el problema sea el mismo. “Something went wrong”, “Unable to fetch” y “Network error” pueden describir un caso, pero se sienten inconsistentes y complican el soporte.
Elige una etiqueta por tipo de error y reutilízala en web y móvil, con el mismo tono y nivel de detalle.
Otro error clásico es mostrar un estado vacío antes de que los datos terminen de cargarse, o mostrar “No items” cuando el verdadero problema es una petición fallida. El usuario toma una acción equivocada (como añadir contenido cuando debería reintentar).
Haz explícito el orden de decisión: primero loading, luego error si falló y finalmente empty solo cuando sepas que la petición tuvo éxito.
Un error sin acción de recuperación crea callejones sin salida. Lo contrario también es común: tres botones que compiten por la atención.
Mantenlo ajustado:
Las pequeñas diferencias se acumulan: estilos de iconos, padding, formas de botones. Aquí también la UI generada por IA puede desviarse si las instrucciones varían por pantalla.
Bloquea espaciado, conjunto de iconos y layout para los componentes de estado para que cada nueva pantalla herede la misma estructura.
Si quieres manejo de estados consistente entre web y móvil, haz explícitas las reglas “aburridas”. La mayoría del pulido final ocurre porque cada pantalla inventa su propio comportamiento de carga, timeouts y etiquetas.
Para una carga de página completa, elige un por defecto: skeletons para pantallas con contenido pesado (listas, tarjetas, dashboards) y un spinner solo para esperas cortas donde el layout es desconocido.
Añade un umbral de tiempo para que la UI no permanezca colgada en silencio. Si la carga tarda más de unos 8 a 10 segundos, cambia a un mensaje claro y una acción visible como “Retry”.
Para cargas parciales, no dejes la pantalla en blanco. Mantén el contenido existente visible y muestra un indicador pequeño de progreso cerca de la sección que se está actualizando (por ejemplo, una barra delgada en el encabezado o un spinner inline).
Para datos en caché, prefiere “obsoleto pero usable”. Muestra contenido en caché de inmediato y añade un sutil indicador de “Refreshing…” para que la gente entienda por qué los datos pueden cambiar.
Offline es su propio estado. Dilo claramente y explica qué sigue funcionando. Ejemplo: “You’re offline. You can view saved projects, but syncing is paused.” Ofrece un único siguiente paso como “Try again” o “Open saved items.”
Mantén esto consistente entre plataformas:
Si generas UI con una herramienta como Koder.ai, incorporar estas reglas en un StateKit compartido ayuda a que cada nueva pantalla sea consistente por defecto.
Imagina un CRM simple con una pantalla de lista de Contacts y una pantalla de detalles de Contact. Si tratas los estados de carga, error y vacío como soluciones únicas, web y móvil se desvían rápido. Un pequeño sistema mantiene las cosas alineadas incluso cuando la UI se produce con rapidez.
Estado vacío inicial (lista de Contacts): el usuario abre Contacts y no ve nada todavía. En web y móvil, el título se mantiene (“Contacts”), el mensaje vacío explica por qué (“No contacts yet”) y se ofrece un siguiente paso claro (“Add your first contact”). Si se requiere configuración (como conectar una bandeja o importar un CSV), el estado vacío apunta a ese paso exacto.
Carga por red lenta: el usuario abre la página de detalles de un Contact. Ambas plataformas muestran un skeleton predecible que coincide con la estructura final de la página (encabezado, campos clave, notas). El botón de volver sigue funcionando, el título de la página es visible y se evitan spinners aleatorios en distintos lugares.
Error de servidor: la petición de detalles falla. El mismo patrón aparece en web y móvil: un titular corto, una frase y una acción primaria (“Retry”). Si reintentar falla otra vez, ofrece una segunda opción como “Go back to Contacts”, para que el usuario no quede atrapado.
Lo que se mantiene consistente es simple:
Un lanzamiento puede parecer “listo” hasta que alguien tiene una conexión lenta, una cuenta nueva o una API inestable. Esta checklist te ayuda a detectar huecos de última milla sin convertir QA en una búsqueda del tesoro.
Empieza por pantallas de listas, porque se multiplican. Elige tres listas comunes (resultados de búsqueda, elementos guardados, actividad reciente) y verifica que todas usen la misma estructura de estado vacío: título claro, una frase útil y una acción primaria.
Asegúrate de que los estados vacíos nunca aparezcan mientras los datos aún se están cargando. Si parpadea “Nothing here yet” por una fracción de segundo y luego aparece contenido, la confianza cae rápido.
Revisa los indicadores de carga por consistencia: tamaño, colocación y una duración mínima sensata para que no parpadeen. Si web muestra un spinner en la barra superior pero móvil muestra un skeleton a pantalla completa para la misma pantalla, parece que son dos productos distintos.
Los errores siempre deben responder “¿y ahora qué?” Cada error necesita un siguiente paso: retry, refresh, cambiar filtros, volver a iniciar sesión o contactar soporte.
Una revisión rápida antes de dar por listo el build:
Si usas un constructor IA como Koder.ai, estos chequeos importan aún más porque las pantallas pueden generarse rápido, pero la consistencia sigue dependiendo de un kit compartido y reglas de copy comunes.
La consistencia es más fácil si forma parte del trabajo diario, no si es una limpieza puntual. Cada nueva pantalla debería usar los mismos patrones sin que alguien tenga que recordar “haz que coincida” al final.
Haz del comportamiento de estado parte de la definición de hecho. Una pantalla no está terminada hasta que tiene un estado de carga, un estado vacío (cuando aplica) y un estado de error con una acción clara.
Mantén las reglas ligeras, pero escríbelas. Un documento corto con algunas capturas y los patrones exactos de copy suele ser suficiente. Trata las nuevas variantes como excepciones. Cuando alguien proponga un nuevo diseño de estado, pregunta si realmente es un caso nuevo o si encaja en el kit.
Si vas a refactorizar muchas pantallas, reduce el riesgo haciéndolo en pasos controlados: actualiza un flujo a la vez, verifica en web y móvil y luego continúa. En Koder.ai, snapshots y rollback pueden hacer los cambios más grandes más seguros, y el modo de planificación puede ayudar a definir el StateKit compartido para que las pantallas generadas sigan tus valores por defecto desde el primer día.
Elige un área esta semana donde los problemas de estado causen arreglos de última hora (a menudo búsqueda, onboarding o feed de actividad). Luego:
Una señal concreta de que funciona: menos tickets “pequeños” como “add retry”, “empty state looks weird” o “loading spinner blocks the page.”
Asigna un propietario único para los estándares de estado (un diseñador, un tech lead o ambos). No necesitan aprobar todo, pero sí proteger el kit de que se vaya fragmentando en variantes nuevas que se parecen pero se comportan distinto y que luego cuesten tiempo.
Empieza por nombrar un pequeño conjunto de estados que usarás en todas partes: carga inicial, actualización, estado vacío base, cero resultados y error. Añade reglas explícitas para offline y redes lentas para que no se traten como errores aleatorios. Una vez que el equipo se ponga de acuerdo en nombres y disparadores, la interfaz será predecible entre pantallas y plataformas.
Crea un pequeño StateKit con tres piezas reutilizables: Loading, Error y Empty. Mantén cada una dirigida por las mismas entradas (título, mensaje corto, una acción principal y detalles opcionales) para que cualquier pantalla pueda usarlo sin inventar nueva UI. Haz que la variante por defecto sea la más fácil de usar para que los equipos dejen de crear soluciones únicas.
Usa un orden de decisión simple: muestra carga hasta que la petición termine, luego muestra error si falló y solo muestra vacío después de una respuesta exitosa sin datos. Esto evita el error común donde “No hay elementos” aparece antes de que el contenido se cargue. También facilita el trabajo de QA porque el comportamiento es consistente en todos lados.
Elige una acción por defecto para cada estado y reutiliza la misma etiqueta y colocación en todas las pantallas. Los errores normalmente tienen “Retry”, los estados vacíos base “Create” (o el siguiente paso de configuración) y los cero resultados “Clear filters”. Cuando la acción primaria es predecible, los usuarios avanzan más rápido y los equipos discuten menos sobre el texto del botón.
Escribe el copy con una plantilla compartida: un título corto que nombre la situación, una frase que lo explique en lenguaje claro y un paso siguiente evidente. Prefiere mensajes específicos como “No pudimos cargar tus proyectos” en vez de vagos como “Algo salió mal”. Mantén el tono calmado y consistente para que web y móvil se sientan como un mismo producto.
Trata offline como su propio estado, no como un error genérico. Muestra contenido en caché si lo tienes, di “Estás sin conexión” claramente y explica qué funciona todavía. Ofrece un único siguiente paso, por ejemplo “Try again”, para que el usuario no tenga que adivinar.
Evita destellos de error rápidos en conexiones lentas esperando un breve periodo antes de cambiar la UI. Si la carga supera un umbral, cambia a un mensaje claro de “Still loading…” y ofrece una acción visible como “Retry”. Esto hace que la app parezca responsiva incluso cuando la red no lo es.
Usa tres variantes de tamaño: inline pequeña (dentro de una tarjeta o sección), sección por defecto y página completa bloqueante. Define cuándo usar cada una para que los equipos no improvisen por pantalla. Mantener el mismo espaciado, estilo de icono y estilo de botón entre variantes es lo que hace que la experiencia sea consistente.
Incluye unas pocas reglas: mueve el foco al mensaje y a la acción principal cuando aparece el estado, anuncia carga y errores con etiquetas claras, y asegúrate de que los botones sean fáciles de tocar. No dependas solo del color o de la animación para comunicar estado. Si esto forma parte del StateKit, cada nueva pantalla lo heredará automáticamente.
Despliega por áreas del producto una a la vez, empezando por listas y pantallas de detalle de alto tráfico. Haz un inventario, elige algunas ubicaciones canónicas, y reemplaza los estados únicos con los componentes compartidos cuando toques cada pantalla. Si generas UI en Koder.ai, añade como regla usar el StateKit por defecto para que las nuevas pantallas no se desvíen.