Aprenda um sistema simples para estados consistentes de carregamento, erro e vazio entre web e mobile, para que UI gerada por IA fique coerente e precise de menos polimento.

Estados de carregamento, erro e vazio são as telas (ou pequenos blocos de UI) que as pessoas veem quando o app está aguardando, algo falhou ou simplesmente não há nada para mostrar. Eles são normais: redes ficam lentas, permissões são negadas e contas novas começam sem dados.
Ficam bagunçados porque geralmente são adicionados no fim e com pressa. Times constroem primeiro o caminho feliz, depois encaixam um spinner, uma mensagem vermelha e um placeholder de “sem itens” onde a UI quebra. Faça isso em dezenas de telas e você acaba com uma pilha de casos únicos.
Iteração rápida piora isso. Quando a UI é produzida rapidamente (incluindo UI gerada por IA), o layout principal pode aparecer em minutos, mas esses estados são fáceis de pular. Cada nova tela acaba com um estilo de spinner diferente, redação diversa (“Try again” vs “Retry”) e posicionamento distinto do botão. A velocidade ganha no início se transforma em trabalho de polimento pouco antes do lançamento.
Estados desencontrados confundem usuários e custam tempo às equipes. As pessoas não conseguem distinguir se uma lista vazia significa “sem resultados”, “ainda não carregou” ou “você não tem acesso”. A QA precisa testar uma longa cauda de variações pequenas, e bugs passam porque o comportamento difere entre web e mobile.
“Bagunça” costuma parecer com isto:
O objetivo é simples: uma abordagem compartilhada entre web e mobile. Se seu time gera recursos rapidamente (por exemplo, usando uma plataforma como Koder.ai), ter um padrão de estados importa ainda mais porque toda nova tela já começa coerente por padrão.
A maioria dos apps repete os mesmos pontos de pressão: listas, páginas de detalhe, formulários, dashboards. É aí que spinners, banners e mensagens de “nada aqui” se multiplicam.
Comece nomeando e padronizando cinco tipos de estado:
Dois casos especiais merecem regras próprias porque se comportam diferente:
Entre telas e plataformas, mantenha a estrutura consistente: onde o estado aparece, o estilo do ícone, o tom e as ações padrão (Retry, Refresh, Clear filters, Create). O que pode variar é o contexto: o nome da tela e uma frase que use as palavras do usuário.
Exemplo: se você gerar uma lista web e uma lista mobile para “Projects”, elas devem compartilhar o mesmo padrão de zero‑results. O rótulo da ação ainda pode casar com a plataforma (“Clear filters” vs “Reset”).
Se cada tela inventa seu próprio spinner, cartão de erro e mensagem vazia, você vai acabar com uma dúzia de versões levemente diferentes. A correção mais rápida é um pequeno “state kit” que qualquer recurso pode usar.
Comece com três componentes reutilizáveis que funcionem em todo lugar: Loading, Error e Empty. Mantenha‑os propositalmente discretos. Devem ser fáceis de reconhecer e não competir com a UI principal.
Torne os componentes previsíveis definindo um pequeno conjunto de entradas:
Então trave a aparência. Decida uma vez espaçamento, tipografia, tamanho do ícone e estilo do botão, e trate isso como regra. Quando o tamanho do ícone e o tipo de botão permanecem os mesmos, os usuários param de notar a UI de estado e passam a confiar nela.
Limite as variantes para que o kit não vire um segundo design system. Três tamanhos costumam cobrir: small (inline), default (seção) e full‑page (bloqueante).
Se você gera telas no Koder.ai, uma instrução simples como “use the app StateKit for loading/error/empty with default variant” evita deriva. Também reduz limpeza de última hora em React web e Flutter mobile.
A cópia faz parte do sistema, não é decoração. Mesmo quando o layout é consistente, frases improvisadas fazem as telas parecerem diferentes.
Escolha uma voz compartilhada: curta, específica, calma. Diga o que aconteceu em termos simples e depois diga ao usuário o que fazer a seguir. A maioria das telas precisa só de um título claro, uma explicação curta e uma ação óbvia.
Alguns padrões de mensagem cobrem a maioria das situações. Mantenha‑os curtos para caber em telas pequenas:
Evite texto vago como “Something went wrong” sozinho. Se você realmente não sabe a causa, diga o que sabe e o que o usuário pode fazer agora. “We couldn’t load your projects” é melhor que “Error.”
Estabeleça uma regra: todo erro e estado vazio oferece um próximo passo.
Isso importa ainda mais com UI gerada por IA, onde telas aparecem rápido. Templates mantêm a cópia consistente para você não reescrever dezenas de mensagens pontuais no polimento final.
Quando telas de estado sugerem ações diferentes de uma página para outra, os usuários hesitam. As equipes então acabam ajustando botões e rótulos pouco antes do lançamento.
Decida qual ação pertence a cada estado e mantenha o posicionamento e o rótulo consistentes. A maioria das telas deve ter uma ação primária. Se adicionar uma segunda, ela deve suportar o caminho principal, não competir com ele.
Mantenha as ações permitidas restritas:
Botões discretos são uma característica. Eles tornam a UI familiar e ajudam telas geradas a permanecer coerentes.
Mostre “Retry” apenas quando retry tiver chance real de funcionar (timeouts, rede instável, 5xx). Adicione um pequeno debounce para que toques repetidos não spammem requisições, e mude o botão para estado de carregamento enquanto tenta novamente.
Após falhas repetidas, mantenha o mesmo botão primário e melhore a ajuda secundária (por exemplo, uma dica “Check connection” ou “Try again later”). Evite introduzir novos layouts só porque algo falhou duas vezes.
Para detalhes de erro, mostre uma razão prática que o usuário possa agir (“Sua sessão expirou. Faça login novamente.”). Oculte detalhes técnicos por padrão. Se precisar deles, coloque‑os atrás de um affordance “Details” consistente entre plataformas.
Exemplo: uma lista “Projects” falha ao carregar no mobile. Ambas as plataformas mostram a mesma ação primária “Retry”, desabilitam enquanto tentam de novo e, após duas falhas, adicionam uma dica pequena sobre conexão em vez de mudar todo o layout do botão.
Trate consistência de estados como uma pequena mudança de produto, não um redesign. Vá incremental e torne a adoção fácil.
Comece com um rápido snapshot do que já existe. Não busque perfeição. Capture as variações comuns: spinners vs skeletons, erros full‑page vs banners, telas de “no results” com tons diferentes.
Um plano de rollout prático:
Quando os componentes existirem, o verdadeiro ganho de tempo é um conjunto curto de regras que remove debate: quando um estado bloqueia a página inteira vs apenas um card, e quais ações devem estar presentes.
Mantenha as regras curtas:
Se você usa um gerador de UI como o Koder.ai, essas regras dão retorno rápido. Você pode pedir “use the state kit components” e obter telas que combinam com seu sistema tanto em React web quanto em Flutter mobile com menos limpeza.
Trabalho de polimento de última hora geralmente acontece porque o tratamento de estados foi feito como gambiarras. Uma tela “funciona”, mas a experiência parece diferente toda vez que algo demora, falha ou não tem dados.
Skeletons ajudam, mas deixá‑los por muito tempo faz as pessoas pensarem que o app travou. Uma causa comum é mostrar um skeleton completo em uma chamada lenta sem sinal de que algo está progredindo.
Defina um limite de tempo: após um pequeno atraso, mude para uma mensagem mais leve “Still loading…” ou mostre progresso quando possível.
Times frequentemente escrevem uma nova mensagem toda vez, mesmo quando o problema é o mesmo. “Something went wrong”, “Unable to fetch” e “Network error” podem descrever um caso só, mas parecem inconsistentes e dificultam o suporte.
Escolha um rótulo por tipo de erro e reaplique‑o em web e mobile, com o mesmo tom e nível de detalhe.
Outro erro clássico é mostrar um estado vazio antes dos dados terminarem de carregar, ou mostrar “No items” quando o problema real é uma requisição falhada. O usuário toma a ação errada (como adicionar conteúdo quando deveria tentar novamente).
Deixe explícita a ordem de decisão: loading primeiro, depois error se houve falha, e só então empty quando você souber que a requisição teve sucesso.
Um erro sem ação de recuperação cria becos sem saída. O oposto também é comum: três botões competindo por atenção.
Mantenha‑o enxuto:
Pequenas diferenças somam: estilo de ícone, padding, formato de botão. Isso também é onde UI gerada por IA pode derivar se prompts variam por tela.
Trave espaçamento, conjunto de ícones e layout para componentes de estado para que cada nova tela herde a mesma estrutura.
Se quiser tratamento de estados consistente entre web e mobile, torne as regras “sem graça” explícitas. A maior parte do polimento tardio acontece porque cada tela inventa seu próprio comportamento de loading, timeouts e rótulos.
Para carregamento full page, escolha um padrão: skeletons para telas ricas em conteúdo (listas, cards, dashboards) e spinner apenas para esperas curtas onde o layout é desconhecido.
Adicione um limite de tempo para que a UI não fique pendurada silenciosamente. Se o carregamento passar de cerca de 8 a 10 segundos, mude para uma mensagem clara e uma ação visível como “Retry.”
Para carregamentos parciais, não deixe a tela em branco. Mantenha o conteúdo existente visível e mostre um indicador de progresso perto da seção que está atualizando (por exemplo, uma barra fina no cabeçalho ou um spinner inline).
Para dados em cache, prefira “stale but usable.” Mostre o conteúdo em cache imediatamente e adicione um sutil indicador “Refreshing…” para que as pessoas entendam que os dados podem mudar.
Offline é um estado próprio. Diga claramente e informe o que ainda funciona. Exemplo: “You’re offline. You can view saved projects, but syncing is paused.” Ofereça um único próximo passo como “Try again” ou “Open saved items.”
Mantenha estas consistentes entre plataformas:
Se você gera UI com uma ferramenta como Koder.ai, incorporar essas regras em um StateKit compartilhado ajuda cada nova tela a ser consistente por padrão.
Imagine um CRM simples com uma tela de Contacts list e uma tela de Contact details. Se tratar loading, error e empty como casos isolados, web e mobile derivam rápido. Um sistema pequeno mantém as coisas alinhadas mesmo quando a UI é produzida com rapidez.
First-time empty state (Contacts list): o usuário abre Contacts e não vê nada ainda. Em web e mobile, o título permanece o mesmo (“Contacts”), a mensagem vazia explica o porquê (“No contacts yet”) e um próximo passo claro é oferecido (“Add your first contact”). Se for preciso alguma configuração (como conectar uma caixa de entrada ou importar CSV), o estado vazio aponta esse passo exato.
Slow network loading: o usuário abre a página de Contact details. Ambas as plataformas mostram um skeleton previsível que corresponde à estrutura final da página (cabeçalho, campos principais, notas). O botão voltar continua funcionando, o título da página é visível e você evita spinners aleatórios em lugares diferentes.
Server error: a requisição de detalhes falha. O mesmo padrão aparece em web e mobile: uma manchete curta, uma frase e uma ação primária (“Retry”). Se o retry falhar novamente, ofereça uma segunda opção como “Go back to Contacts”, para que o usuário não fique preso.
O que permanece consistente é simples:
Um release pode parecer “pronto” até alguém cair numa conexão lenta, numa conta nova ou numa API instável. Este checklist ajuda a identificar lacunas de última hora sem transformar a QA numa caça ao tesouro.
Comece por telas de lista, porque elas se multiplicam. Escolha três listas comuns (resultados de busca, itens salvos, atividade recente) e verifique se todas usam a mesma estrutura de estado vazio: título claro, uma frase útil e uma ação primária.
Assegure que estados vazios nunca apareçam enquanto os dados ainda estão carregando. Se você piscar “Nothing here yet” por um split second e depois substituir por conteúdo, a confiança cai rápido.
Cheque indicadores de loading por consistência: tamanho, posição e uma duração mínima sensata para que não pisquem. Se web mostra um spinner na barra superior mas mobile mostra um skeleton full‑screen para a mesma tela, parece que são dois produtos diferentes.
Erros devem sempre responder “e agora?”. Todo erro precisa de um próximo passo: retry, refresh, limpar filtros, logar novamente ou contatar suporte.
Uma verificação rápida antes de considerar o build pronto:
Se você usa um construtor de IA como o Koder.ai, essas verificações valem ainda mais porque telas podem ser geradas rápido, mas a consistência depende de um kit compartilhado e de regras de cópia.
Consistência é mais fácil quando faz parte do dia a dia, não de uma limpeza pontual. Toda nova tela deve usar os mesmos padrões sem que alguém lembre “faça combinar” no final.
Faça o comportamento de estado parte da definição de pronto. Uma tela não está finalizada até ter um estado de loading, um estado vazio (quando aplicável) e um estado de erro com uma ação clara.
Mantenha as regras leves, mas documente. Um documento curto com algumas capturas e os padrões exatos de cópia costuma bastar. Trate novas variantes como exceção. Quando alguém propuser um novo design de estado, pergunte se é realmente um caso novo ou se cabe no kit.
Se for refatorar muitas telas, reduza risco fazendo em passos controlados: atualize um fluxo por vez, verifique em web e mobile e continue. No Koder.ai, snapshots e rollback podem tornar mudanças maiores mais seguras, e o planning mode ajuda a definir o StateKit compartilhado para que telas novas sigam seus padrões desde o início.
Escolha esta semana uma área onde problemas de estado geram correções de última hora (muitas vezes busca, onboarding ou feed de atividade). Então:
Um sinal concreto de que está funcionando: menos tickets pequenos como “adicionar retry”, “estado vazio estranho” ou “spinner bloqueia a página”.
Atribua um único proprietário para padrões de estado (um designer, um tech lead, ou ambos). Não precisam aprovar tudo, mas devem proteger o kit de se fragmentar em novas variantes que parecem similares, se comportam diferente e custam tempo depois.
Comece nomeando um pequeno conjunto de estados que você usará em todos os lugares: initial loading, refreshing, empty baseline, zero results e error. Adicione regras explícitas para offline e rede lenta para que não sejam tratados como erros aleatórios. Depois que a equipe concordar com os nomes e gatilhos, a UI fica previsível entre telas e plataformas.
Construa um StateKit mínimo com três peças reutilizáveis: Loading, Error e Empty. Mantenha cada componente acionado pelos mesmos inputs (título, mensagem curta, uma ação primária e detalhes opcionais) para que qualquer tela consiga usá‑lo sem inventar nova UI. Faça a variante padrão a mais simples de usar para reduzir one-offs.
Use uma ordem simples de decisão: mostre loading até a requisição terminar, então mostre error se falhou, e só mostre empty depois de uma resposta bem‑sucedida sem dados. Isso evita o bug comum em que “Sem itens” aparece brevemente antes do conteúdo carregar. Também ajuda a QA porque o comportamento fica consistente.
Escolha uma ação padrão por estado e reutilize o mesmo rótulo e posicionamento entre telas. Erros normalmente recebem “Retry”, estados vazios recebem “Create” (ou o próximo passo de configuração), e zero results recebem “Clear filters”. Quando a ação primária é previsível, usuários agem mais rápido e as equipes perdem menos tempo discutindo rótulos.
Escreva cópia num modelo compartilhado: um título curto que nomeie a situação, uma frase que explique em linguagem simples e um próximo passo claro. Prefira mensagens específicas como “Não foi possível carregar seus projetos” em vez de “Algo deu errado”. Mantenha o tom calmo e uniforme para que web e mobile pareçam o mesmo produto.
Trate offline como um estado próprio, não um erro genérico. Mostre conteúdo em cache quando houver, diga “Você está offline” claramente e explique o que ainda funciona no momento. Ofereça um único próximo passo como “Try again” para que o usuário não fique sem saber o que fazer.
Evite flashes rápidos de erro em conexões lentas esperando um pequeno intervalo antes de mudar a UI. Se o carregamento ultrapassar um limiar, troque para uma mensagem clara do tipo “Still loading…” e ofereça uma ação visível como “Retry.” Isso faz o app parecer responsivo mesmo com rede ruim.
Use três variantes de tamanho: inline pequeno (dentro de um card ou seção), seção padrão e full‑page/bloqueante. Defina quando cada uma é permitida para que as equipes não improvisem por tela. Manter o mesmo espaçamento, estilo de ícone e tipo de botão entre variantes é o que torna a experiência consistente.
Inclua algumas regras básicas: mova o foco para a mensagem e ação primária quando o estado aparecer, anuncie loading e erros com rótulos curtos e claros, e garanta que botões sejam fáceis de tocar. Não dependa só de cor ou animação para comunicar status. Se isso fizer parte do StateKit, toda nova tela herda automaticamente essas regras.
Faça o rollout por área do produto, começando por listas e telas de detalhe de alto tráfego. Faça um inventário do que existe, escolha algumas colocações canônicas e vá substituindo estados one‑off pelos componentes compartilhados conforme tocar cada tela. Se você gera UI no Koder.ai, inclua uma instrução fixa para usar o StateKit por padrão e evitar deriva.