As Cinco Telas Que Ninguém Desenha (Mas Todo Cliente Vê)
Tela vazia, carregando, erro e sem conexão: os quatro estados que ninguém desenha no layout, mas que decidem se o produto parece amador ou profissional.
Renan Montanari
Engenheiro de Software Full-Stack
Introdução: A Tela Que Aprova e as Quatro Que Importam
No layout existe uma tela. No sistema real existem cinco.
A que todo mundo desenha é a tela cheia, bonita, com dados de exemplo perfeitos. É essa que é aprovada na reunião com stakeholders. É essa que entra no orçamento do sprint. É essa que vai para o portfolio.
As outras quatro? Só aparecem depois. Com cliente dentro. E é nessas quatro que o produto vira amador ou profissional.
Curiosamente, ninguém desenha as outras quatro deliberadamente. Elas simplesmente "acontecem", muitas vezes quebradas, confusas ou invisíveis.
As Quatro Telas Invisíveis (Que Todo Dia Alguém Usa)
1️⃣ A Tela Vazia
O usuário acabou de criar a conta. Clicou em "Nova lista", "Novo projeto", "Novo espaço de trabalho".
E viu um espaço em branco.
Se ali só existir branco, a pessoa sai. Ela não entende o que fazer. Não porque é burra, porque você não mostrou o caminho.
Esse é o momento mais crítico do produto inteiro. E é quase sempre o mais abandonado.
❌ Amador:
[espaço branco]
[espaço branco]
[espaço branco]✅ Profissional:
- Mensagem clara: "Você ainda não criou nada aqui"
- Próximo passo óbvio: botão destacado "Criar seu primeiro [item]"
- Atalho visual: ícone ou ilustração que explica o que é aquele espaço
- Sugestão de exemplo: "Veja como funciona" com dados de exemplo
- Onboarding contextual: tooltip ou mini-tutorial se for a primeira vez
// Componente EmptyState profissional
<EmptyState
icon={<FilePlus size={48} />}
title="Nenhum projeto ainda"
description="Crie seu primeiro projeto para começar a organizar tarefas"
action={{
label: "Criar primeiro projeto",
onClick: handleCreateProject,
}}
secondaryAction={{
label: "Ver exemplo",
onClick: handleShowExample,
}}
/>Por que funciona: remove fricção. O usuário sabe exatamente o que fazer agora.
Métrica real: empty states bem feitos reduzem churn em 15-25% no primeiro dia.
2️⃣ A Tela Carregando
O servidor está processando algo. Uma requisição está sendo feita. Um arquivo está sendo salvo.
Se nada indica que algo está acontecendo, a pessoa clica de novo.
E clicar de novo é exatamente como nascem cobranças em dobro.
O Problema Real:
[usuário clica "Salvar"]
[tela congela]
[aparentemente nada acontece]
[usuário clica "Salvar" de novo]
[5 segundos depois, dois pagamentos processados]
[cliente liga furioso]✅ Estados de Carregamento Profissionais:
1. Feedback visual imediato:
// Mostrar feedback no botão IMEDIATAMENTE
<button onClick={handleSave} disabled={isSaving}>
{isSaving ? (
<>
<Spinner size="sm" /> Salvando...
</>
) : (
"Salvar"
)}
</button>2. Loading skeleton (melhor que spinner):
// Em vez de "carregando...", mostra a forma do conteúdo
<SkeletonLoader>
<SkeletonLine width="60%" />
<SkeletonLine width="40%" />
<SkeletonBlock height={100} />
</SkeletonLoader>3. Desabilitar ações duplicadas:
// Previne múltiplos cliques
const handleSave = async () => {
if (isSaving) return; // Ignora cliques subsequentes
setIsSaving(true);
try {
await api.save(data);
} finally {
setIsSaving(false);
}
};4. Timeout + fallback:
// Se demorar mais de 10s, algo está errado
const handleSaveWithTimeout = async () => {
const timeoutId = setTimeout(() => {
showAlert("Operação demorando mais que o normal...", "warning");
// Opção de tentar novamente ou cancelar
}, 10000);
try {
await api.save(data);
} catch (error) {
showAlert("Falha ao salvar. Tente novamente.", "error");
} finally {
clearTimeout(timeoutId);
}
};Métrica real: loading states claros reduzem "cliques duplos" em 80%.
3️⃣ A Tela de Erro
O pagamento falhou. A integração devolveu 500. A rede caiu na hora errada.
"Algo deu errado"
Essa frase não ajuda ninguém. Nem o usuário (que não sabe o que fazer) nem você (que não consegue debugar).
❌ Erro amador:
Error: 500 Internal Server Error
[OK]✅ Erro profissional:
🚨 Não conseguimos processar seu pagamento
O seu cartão foi recusado pela operadora.
Isso pode acontecer por:
- Saldo insuficiente
- Cartão bloqueado por segurança
- Dados do cartão incorretos
O que fazer agora:
• Verifique seu saldo
• Tente outro cartão
• Contate seu banco
[Tentar novamente] [Usar outro método de pagamento]
Precisa de ajuda? Email: support@...
Ref: #ERR-5f2e3a1b (para suporte)Implementação:
const getErrorMessage = (error) => {
switch (error.code) {
case "CARD_DECLINED":
return {
title: "Cartão recusado",
description: "A operadora bloqueou a transação",
suggestions: [
"Verifique o saldo",
"Tente outro cartão",
"Contate seu banco",
],
action: "Tentar novamente",
};
case "INSUFFICIENT_FUNDS":
return {
title: "Saldo insuficiente",
description: `Sua conta tem ${error.details.balance}, mas precisa de ${error.details.required}`,
suggestions: ["Adicione crédito", "Use outro método"],
action: "Adicionar crédito",
};
case "NETWORK_ERROR":
return {
title: "Sem conexão",
description: "Sua internet caiu no meio da operação",
suggestions: ["Conecte ao Wi-Fi", "Tente o 4G"],
action: "Tentar novamente",
};
default:
return defaultError;
}
};
const ErrorDisplay = ({ error, onRetry }) => {
const message = getErrorMessage(error);
return (
<div className="error-container">
<h2>{message.title}</h2>
<p>{message.description}</p>
<ul className="suggestions">
{message.suggestions.map((s) => (
<li key={s}>{s}</li>
))}
</ul>
<button onClick={onRetry}>{message.action}</button>
<small>ID: {error.id}</small>
</div>
);
};Por que funciona: o usuário sabe exatamente o que aconteceu e o que fazer. Você tem contexto para debugar (o ID do erro). Ninguém se sente culpado.
4️⃣ A Tela Sem Conexão
O celular entrou no elevador. A rede sumiu por 3 segundos. O que o usuário tinha digitado se perdeu.
Esse é o tipo de coisa que usuário não perdoa.
O Problema:
[usuário digita comentário longo no trem]
[conexão cai por 2 segundos]
[tela volta em branco]
[usuário precisa reescrever tudo]
[usuário desinstala o app]✅ Solução Profissional: Offline-First
1. Detectar perda de conexão:
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}, []);2. Salvar rascunho localmente:
const handleCommentChange = (text) => {
setCommentText(text);
// Salva no localStorage enquanto digita
localStorage.setItem("pending_comment", text);
};
const handleSendComment = async () => {
try {
await api.createComment(commentText);
localStorage.removeItem("pending_comment");
} catch (error) {
// Não apaga do localStorage, vai tentar de novo depois
showAlert("Pendente de envio. Vamos tentar quando conectar.");
}
};3. Fila de sincronização:
// Quando a conexão volta, sincroniza os pendentes
useEffect(() => {
if (isOnline) {
syncPendingRequests();
}
}, [isOnline]);
const syncPendingRequests = async () => {
const pending = localStorage.getItem("pending_comment");
if (pending) {
try {
await api.createComment(pending);
localStorage.removeItem("pending_comment");
showAlert("✓ Comentário enviado!");
} catch {
// Falhou de novo, deixa para a próxima vez
}
}
};4. Indicador visual claro:
<div className={isOnline ? "online" : "offline"}>
{!isOnline && (
<Banner>
📡 Sem conexão. Suas alterações foram salvas
e serão sincronizadas quando conectar.
</Banner>
)}
</div>Métrica real: apps com suporte offline têm 40% menos abandono em redes lentas.
Por Que Ninguém Desenha Essas Quatro Telas?
Tem algumas razões:
1. Parecem "casos especiais"
Não parecem features. Parecem bugs. Então ninguém prioriza.
2. Não cabem no protótipo
O Figma não tem "conectar e desconectar do Wi-Fi". Não há storyboard de erro. Então ficam para depois.
3. Não têm data de entrega
A tela cheia? "Segunda-feira". As outras quatro? "Quando der". Resultado: nunca dá.
4. Designer → Dev → Cliente
O designer desenha a tela cheia. O dev implementa. O cliente vê e aprova. Ninguém vê as outras quatro até estar em produção.
O Teste de Dez Segundos
Abra seu produto agora.
Desligue o Wi-Fi.
O que aparecer é a parte que ninguém desenhou.
Agora responda com sinceridade:
- ❌ Desapareceu?
- ❌ Ficou congelado?
- ❌ "Algo deu errado"?
- ✅ "Desconectado: suas alterações foram salvas"?
Checklist: As Cinco Telas
Antes de considerar uma feature pronta, confira se ela existe em:
- Tela cheia: com dados reais/de exemplo
- Tela vazia: novo usuário, sem dados ainda
- Tela carregando: requisição processando, feedback visual
- Tela de erro: com contexto, não genérica
- Tela offline: sem conexão, dados salvos localmente
Três ou mais caixas sem marcar significa que ainda falta trabalho.
A Diferença Entre Profissional e Improvisado
Quase nunca está na tela que foi aprovada.
Está nas outras quatro.
Um produto que "parece profissional" é aquele em que você:
- Nunca se sente perdido (empty states)
- Nunca clica duas vezes por acidente (loading states)
- Entende exatamente o que deu errado (mensagens de erro)
- Não perde seu trabalho (suporte offline)
E nada disso é "design bonito". É design pensado para a realidade.
Como Começar: Uma Sprint Para Arrumar Tudo
Sprint 0 (Auditoria, 1 dia):
- Liste todos os estados possíveis do seu app
- Tire um screenshot de cada um
- Identifique os que estão quebrados
Sprint 1 (Empty States, 3 dias):
- Redesenhe cada tela vazia
- Adicione CTAs claros
- Teste com um usuário novo de verdade
Sprint 2 (Tratamento de Erros, 3 dias):
- Mapeie cada erro possível
- Crie mensagens específicas
- Adicione IDs de erro para o suporte debugar
Sprint 3 (Offline + Loading, 5 dias):
- Implemente offline-first
- Adicione loading states
- Teste com conexão simulada (DevTools)
Próximos Passos
Se seu produto tem as cinco telas bem desenhadas, ele já está na frente de 80% dos produtos por aí.
Se ainda falta trabalho, comece pela mais crítica: a tela vazia. É onde mais usuários saem.
Depois, a tela sem conexão. É onde mais reclamações chegam.
Precisa de Ajuda Para Auditar Suas Telas?
Se você está construindo um SaaS ou produto digital e quer garantir que cada estado da jornada do usuário seja profissional, tenho estrutura para isso:
- UX Audit Completo: análise de todos os estados
- Design System de Estados: componentes reutilizáveis para cada cenário
- Implementação End-to-End: do design à produção
Falar comigo