Estados de carga, error y vacío consistentes entre web y móvil
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.

Por qué estos estados se vuelven desordenados tan rápido
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í:
- La carga oculta acciones en una pantalla pero no en otra.
- Los errores a veces bloquean la página y otras veces aparecen como texto pequeño.
- Los estados vacíos usan distintos tonos, iconos y siguientes pasos.
- Web y móvil usan etiquetas distintas para la misma acción.
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.
Los 5 tipos de estado que vale la pena estandarizar primero
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:
- Carga inicial: la primera vez que se abre una pantalla, antes de tener datos.
- Actualización / refresco: la pantalla ya tiene datos, pero estás pidiendo datos más recientes.
- Estado vacío base: realmente no hay nada aún (cuenta nueva, espacio de trabajo nuevo, ningún elemento creado).
- Cero resultados: hay datos, pero filtros o búsqueda no devuelven nada.
- Error: las peticiones fallan, permisos bloquean el acceso o algo se rompe.
Dos casos especiales merecen reglas propias porque se comportan diferente:
- Offline: muestra contenido en caché cuando sea posible y di “You’re offline” claramente.
- Red lenta: evita destellos rápidos de error; tras una breve demora, cambia a “Still loading…” (o similar).
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”).
Construye un pequeño kit de estados en vez de soluciones únicas
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:
- Título (corto y específico)
- Mensaje (una o dos frases)
- Etiqueta de la acción primaria
- Manejador de la acción primaria (retry, refresh o siguiente paso)
- Detalles opcionales (código de error, acción secundaria)
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.
Escribe copys de estado que se mantengan consistentes
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.
Usa plantillas para que los equipos dejen de improvisar
Algunos patrones de mensaje cubren la mayor parte de situaciones. Mantenlos cortos para que quepan en pantallas pequeñas:
- Network: “Can’t connect” + “Check your internet and try again.” + Acción: “Retry”
- Timeout: “Taking longer than expected” + “The request timed out.” + Acción: “Try again”
- Permission: “Permission needed” + “Allow access to continue.” + Acción: “Open settings”
- Not found: “Nothing here” + “This item may have been removed.” + Acción: “Back”
- Validation: “Fix one thing” + “Add a valid email address.” + Acción: “Save”
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.”
Haz que el siguiente paso sea predecible
Aplica una regla: cada error y estado vacío ofrece un siguiente paso.
- Si el usuario puede recuperarse, ofrece “Retry” o “Refresh”.
- Si el estado está vacío por filtros, sugiere “Clear filters”.
- Si el usuario está bloqueado (permiso), dile exactamente dónde cambiarlo.
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.
Haz las acciones predecibles: retry, refresh y siguientes pasos
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.
Un pequeño mapa de acciones que escala
Mantén las acciones permitidas ajustadas:
- Loading: normalmente sin acción (opcional “Cancel” para tareas largas iniciadas por el usuario).
- Empty: “Create” cuando el usuario puede añadir algo; “Adjust filters” cuando los filtros causaron el vacío.
- Error: “Retry” para fallos temporales; “Refresh” para datos obsoletos; “Contact support” solo para flujos bloqueados.
- Success sin cambio de datos: “Back” o “Done.”
Los botones aburridos son una ventaja. Hacen la UI familiar y ayudan a que las pantallas generadas permanezcan coherentes.
Reglas para Retry y detalles de error
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.
Despliega el sistema sin frenar a los equipos
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:
- Haz inventario de 15 a 30 pantallas clave en web y móvil y agrupa los patrones más frecuentes.
- Elige 3 a 4 colocaciones canónicas que soportarás primero (página completa, sección, inline, toast).
- Construye componentes coincidentes para web y móvil que compartan las mismas props y nombres.
- Escribe reglas de uso cortas para que las nuevas pantallas escojan del kit en vez de inventar una versión.
- Reemplaza un área de producto a la vez (búsqueda, bandeja, facturación) para que los lanzamientos sean seguros.
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:
- Todo error muestra un siguiente paso claro (retry, refresh o contactar soporte).
- Los estados vacíos ofrecen una acción primaria (create, import o explore).
- Los estados de carga no cambian el layout al llegar los datos.
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.
Errores comunes que generan trabajo de pulido al final
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.
Carga que parece atascada
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.
Deriva de copy entre pantallas
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.
Vacío vs error vs no cargado aún
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.
Acciones que faltan o están sobrecargadas
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:
- Para errores, por defecto una acción primaria (normalmente “Retry”).
- Para estados vacíos, una siguiente paso claro.
- Usa acciones secundarias solo cuando sea realmente necesario.
Desajustes visuales entre plataformas
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.
Reglas prácticas para comportamiento de carga y accesibilidad
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.
Reglas de carga que se sienten rápidas (incluso cuando no lo son)
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.”
Fundamentos de accesibilidad que puedes aplicar en todas partes
Mantén esto consistente entre plataformas:
- Orden de foco: mueve el foco al mensaje de estado y a la acción primaria cuando el estado aparece.
- Etiquetas para lectores de pantalla: anuncia carga y errores con texto claro y corto.
- Objetivos táctiles: haz botones fáciles de tocar en móvil, evita enlaces inline pequeños.
- Movimiento: mantiene spinners sutiles y no dependas solo de la animación.
- Color: nunca uses color como única señal (combínalo con texto e iconos).
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.
Ejemplo: una función, tres estados, dos plataformas
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:
- Colocación: mensaje en el área de contenido, acciones en un lugar obvio.
- Copy: un tono, mismas etiquetas de botón (Retry, Add contact).
- Comportamiento: mismos disparadores para skeletons, mismas reglas de retry.
- Seguridad: deshabilitar acciones duplicadas mientras se carga.
Lista rápida antes de enviar
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.
Chequeos de consistencia UI
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.
Chequeos de errores y QA
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:
- Estados vacíos: mismo layout, estilo de icono y tono entre pantallas de lista.
- Carga: nunca reemplazada por vacío demasiado pronto; los indicadores coinciden entre web y móvil.
- Errores: una acción siguiente clara en cada pantalla o toast de error.
- Reglas de copy: patrones de redacción consistentes entre plataformas (títulos, puntuación, verbos de botones).
- Script de QA: un conjunto corto y repetible de pasos que desencadenen cargas, vacíos y errores.
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.
Próximos pasos: mantener la consistencia a medida que la app crece
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.
Una forma práctica de avanzar
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:
- Añade requisitos de estado a la checklist de aceptación para esa área.
- Reemplaza estados únicos por tus componentes compartidos.
- Registra 2 o 3 ejemplos (buenos y malos) en el documento.
- Haz una revisión rápida entre plataformas (mismo significado, mismas acciones, layout similar).
- Mide cuántas correcciones de UI pequeñas abres en el próximo sprint y compáralo.
Una señal concreta de que funciona: menos tickets “pequeños” como “add retry”, “empty state looks weird” o “loading spinner blocks the page.”
Mantén clara la responsabilidad
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.
Preguntas frecuentes
What state types should we standardize first?
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.
How do we avoid dozens of one-off spinners and empty screens?
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.
How do we stop empty states from showing before data is actually loaded?
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.
What should the primary action be on loading, error, and empty states?
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.
How do we keep loading/error/empty copy consistent across the app?
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.
What’s the right way to handle offline mode?
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.
How should we handle slow networks without making the app feel broken?
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.
Where should state UI appear: inline, section, or full-page?
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.
What are the must-have accessibility rules for these states?
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.
How can we roll this out without slowing down feature delivery?
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.