30 Regras de UX para React
Regras para construir interfaces React que pareçam rápidas, tolerantes e intuitivas. Cobre estados de carregamento, recuperação de erros, feedback, acessibilidade e design de interação.
Busque em todas as páginas da documentação
Regras para construir interfaces React que pareçam rápidas, tolerantes e intuitivas. Cobre estados de carregamento, recuperação de erros, feedback, acessibilidade e design de interação.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
1. Mostre algo imediatamente. Nunca mostre uma tela em branco. Use esqueletos de carregamento, conteúdo de placeholder ou dados antigos em cache enquanto os novos dados carregam.
<Suspense fallback={<ProductSkeleton />}>
<ProductDetails id={id} />
</Suspense>2. Use atualizações otimistas para ações iniciadas pelo usuário. Quando um usuário clica em "curtir", "salvar" ou "excluir", atualize a UI instantaneamente. Reverte se o servidor rejeitar. Esperar pelo servidor faz seu aplicativo parecer lento.
const [optimisticLikes, addLike] = useOptimistic(
likes,
(state, newLike: Like) => [...state, newLike]
);3. Mostre estados pendentes em botões e formulários. Desabilite o botão de envio e mostre um spinner durante o envio. Os usuários nunca devem se perguntar se o clique foi registrado.
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending} type="submit">
{pending ? "Salvando..." : "Salvar"}
</button>
);
}4. Use transições para atualizações não urgentes. Envolva atualizações de estado custosas em useTransition para manter a UI responsiva. O conteúdo anterior permanece interativo enquanto o novo conteúdo é renderizado.
const [isPending, startTransition] = useTransition();
function handleSearch(query: string) {
startTransition(() => {
setSearchResults(filterLargeList(query));
});
}5. Pré-carregue rotas que o usuário provavelmente visitará. O Link do Next.js pré-carrega por padrão ao passar o mouse. Para navegação programática, use router.prefetch("/target").
6. Transmita conteúdo progressivamente. Não espere por todos os dados antes de mostrar algo. Use múltiplos limites de Suspense para que cada seção apareça assim que seus dados estiverem prontos.
7. Use debounce em inputs de busca. A digitação dispara a cada pressionamento de tecla. Use debounce com um atraso de 300ms para evitar requisições excessivas e instabilidade na UI.
8. Evite layout shift. Reserve espaço para imagens (defina dimensões), fontes (use next/font) e conteúdo dinâmico (defina min-height). Os usuários nunca devem perder sua posição de rolagem porque o conteúdo mudou.
9. Cache agressivamente, invalide precisamente. Use SWR, TanStack Query ou o cache do Next.js para mostrar dados antigos instantaneamente enquanto revalida em segundo plano. Os usuários veem o conteúdo imediatamente.
10. Forneça feedback instantâneo para cada interação. Estados de hover em botões, estados ativos em links, anéis de foco em inputs, destaques de seleção em toggles. Cada clique e hover deve produzir feedback visível.
11. Mostre erros inline, não caixas de alerta. Exiba erros de validação ao lado do campo relevante. Nunca use alert() ou toast genérico para validação de formulário.
<div>
<label htmlFor="email">Email</label>
<input id="email" aria-invalid={!!errors.email} aria-describedby="email-error" />
{errors.email && (
<p id="email-error" className="text-sm text-red-600">{errors.email}</p>
)}
</div>12. Torne os erros recuperáveis. Cada estado de erro deve incluir um caminho a seguir: um botão de tentar novamente, um link para voltar ou uma sugestão do que tentar a seguir.
13. Preserve a entrada do usuário em caso de erros. Se o envio do formulário falhar, nunca limpe o formulário. Mantenha todos os dados inseridos e destaque apenas os campos que precisam de correção.
14. Use notificações toast para operações em segundo plano. Confirmações de salvamento, conclusões de tarefas assíncronas e erros não bloqueantes pertencem aos toasts. Mantenha-os breves (menos de 5 segundos) com uma ação quando relevante.
15. Trate estados vazios com atenção. Uma lista vazia não é um erro. Mostre uma mensagem útil com uma chamada para ação.
{items.length === 0 ? (
<div className="text-center py-12">
<p className="text-muted-foreground">Ainda não há projetos</p>
<Button onClick={onCreate}>Crie seu primeiro projeto</Button>
</div>
) : (
<ProjectList items={items} />
)}16. Mostre confirmação para ações destrutivas. Excluir, cancelar assinatura, remover membro da equipe. Estas precisam de um diálogo de confirmação com consequências claras declaradas.
17. Ofereça desfazer em vez de confirmação quando possível. "Mensagem excluída. Desfazer" é uma UX melhor do que "Tem certeza de que deseja excluir?". O usuário pode prosseguir mais rapidamente e os erros são recuperáveis.
18. Lide com o modo offline graciosamente. Detecte o estado offline com navigator.onLine e os eventos online/offline. Mostre um banner, enfileire mutações e sincronize ao reconectar.
19. Use HTML semântico primeiro. button para ações, a para navegação, nav para navegação, main para conteúdo principal, h1-h6 em ordem. HTML semântico é acessível por padrão.
20. Todo elemento interativo deve ser acessível pelo teclado. A ordem de tabulação deve ser lógica. Enter/Espaço ativa botões. Escape fecha modais. As setas navegam em menus. Teste sem mouse.
21. Toda imagem precisa de um atributo alt. alt descritivo para imagens de conteúdo. alt="" vazio para imagens decorativas. Nunca omita o atributo.
| Tipo de Imagem | Texto Alt |
|---|---|
| Conteúdo (foto, gráfico) | Descreva o que a imagem mostra |
| Decorativa (fundo, divisor) | alt="" (string vazia) |
| Funcional (ícone de botão) | Descreva a ação: "Fechar menu" |
22. A cor sozinha não deve transmitir significado. Estados de erro precisam de ícones ou texto além da cor vermelha. Estados de sucesso precisam de mais do que verde. Considere usuários daltônicos (8% dos homens).
23. Gerencie o foco em mudanças de rota e modais. Quando um modal abre, mova o foco para o primeiro elemento focável dentro dele. Quando fecha, retorne o foco para o gatilho. Na navegação do lado do cliente, foque no conteúdo principal ou no título.
24. Anuncie mudanças de conteúdo dinâmico. Use aria-live="polite" para atualizações de status (notificações toast, contagens de resultados de busca) para que os leitores de tela as anunciem.
<div aria-live="polite" aria-atomic="true">
{results.length} resultados encontrados
</div>25. Rotule todos os controles de formulário. Cada input precisa de um label visível ou aria-label. O texto placeholder não é um rótulo. Associe rótulos com htmlFor correspondendo ao id do input.
26. Torne as áreas clicáveis grandes o suficiente. Alvos de toque devem ter pelo menos 44x44px (WCAG). Botões e links pequenos frustram usuários de dispositivos móveis.
// Bom: botão com padding
<button className="px-4 py-3 min-h-[44px]">Salvar</button>
// Ruim: alvo minúsculo
<button className="px-1 py-0.5 text-xs">x</button>27. Mostre estados de carregamento no contexto. Um spinner dentro do botão que foi clicado é melhor do que uma tela de carregamento completa. Mostre o progresso onde o usuário está olhando.
28. Use divulgação progressiva. Não sobrecarregue os usuários com todas as opções de uma vez. Mostre o essencial primeiro, revele opções avançadas sob demanda (acordeão, "Mostrar mais", abas).
<div>
<BasicSettings />
<details>
<summary className="cursor-pointer text-sm text-blue-600">
Configurações avançadas
</summary>
<AdvancedSettings />
</details>
</div>29. Respeite as preferências do usuário. Honre prefers-reduced-motion para animações, prefers-color-scheme para modo escuro e prefers-contrast para alto contraste. Estas são configurações em nível de sistema.
// CSS
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}// Hook React
const prefersReducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)");30. Seja consistente. Ações iguais devem parecer iguais em todos os lugares. Se "Salvar" é um botão azul em uma página, deve ser um botão azul em todas as páginas. Posicionamento consistente, estilo consistente, comportamento consistente. A consistência reduz a carga cognitiva.
useOptimistic mostra um resultado imediato, assumido como bem-sucedido, enquanto uma Server Action é executada (reverte em caso de falha)useTransition mantém a UI atual interativa enquanto uma atualização de estado não urgente é renderizada em segundo planouseOptimistic para mutações (como, curtir); use useTransition para filtragem ou navegação custosasfunction SubmitButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending} type="submit">
{pending ? "Salvando..." : "Salvar"}
</button>
);
}useFormStatus lê o estado pendente do <form> pai mais próximo.
alert() bloqueia a thread e não fornece contexto sobre qual campo falhouaria-describedby para acessibilidade de leitores de telapx-4 py-3 min-h-[44px]) mesmo em botões pequenos<div aria-live="polite" aria-atomic="true">
{results.length} resultados encontrados
</div>Use aria-live="polite" para atualizações não urgentes (contagens de busca, toasts) para que o leitor de tela as anuncie após a fala atual.
<label> visível com htmlFor ou aria-label no inputfunction useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState<boolean>(false);
useEffect(() => {
const mql = window.matchMedia(query);
setMatches(mql.matches);
const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
mql.addEventListener("change", handler);
return () => mql.removeEventListener("change", handler);
}, [query]);
return matches;
}type ErrorFallbackProps = {
error: Error;
resetErrorBoundary: () => void;
};
function ErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) {
return (
<div role="alert">
<p>Algo deu errado: {error.message}</p>
<button onClick={resetErrorBoundary}>Tentar novamente</button>
</div>
);
}prefers-reduced-motion: reduce -- desabilitar ou minimizar animaçõesprefers-color-scheme: dark -- oferecer um modo escuroprefers-contrast: more -- aumentar o contraste para usuários com baixa visão| Categoria | Regra Chave |
|---|---|
| Velocidade | Mostre algo imediatamente. Atualizações otimistas. Transmita progressivamente. |
| Erros | Erros inline. Preserve a entrada. Sempre ofereça recuperação. |
| Acessibilidade | HTML semântico. Acessível pelo teclado. Gerenciamento de foco. |
| Feedback | Estados pendentes em botões. Toast para operações em segundo plano. Desfazer em vez de confirmar. |
| Toque | Alvos mínimos de 44px. Carregamento ciente do contexto. Divulgação progressiva. |
Revisado por Chris St. John·Última atualização: 7 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥