Web UI Progressiva
Performance percebida é sobre fazer a UI parecer rápida, não apenas medir a velocidade real. Uma página que carrega em 2s, mas mostra um esqueleto imediatamente, parece mais rápida do que uma que carrega em 1,5s, mas mostra uma tela em branco o tempo todo.
Quando o usuário compartilha código:
- Leia-o para entender o que os usuários veem atualmente durante os estados de carregamento e espera
- Identifique qual(is) técnica(s) se aplica(m) usando o guia de decisão abaixo
- Explique o que mudar, por que isso melhora a velocidade percebida e como implementar com código concreto
Princípios Fundamentais
1. Separe a "primeira visualização" de todo o resto
Sempre distinga:
- O que é necessário para a primeira pintura
- O que pode carregar após a casca (shell) estar visível
- O que pode esperar pela interação do usuário
Não trate todos os dados como igualmente urgentes.
2. Estrutura antes dos dados
A casca da página (layout, slots de navegação, cabeçalho) deve ser enviada ao navegador antes que qualquer busca de dados seja concluída. Os usuários formam impressões de velocidade nos primeiros 100–200ms. Mostrar a estrutura imediatamente reduz a espera percebida, mesmo que o tempo real de carregamento seja idêntico.
3. Nunca deixe um clique sem resposta
Cada interação (clique, toque, navegação) deve produzir uma resposta visível no primeiro quadro. Se a operação real leva tempo, mostre um estado intermediário imediatamente.
4. Corrija a estrutura, não apenas a UI de carregamento
loading.tsx e esqueletos melhoram a aparência, mas não corrigem o excesso de busca ou dependências de dados sequenciais. Sempre aborde a causa raiz:
- Cadeias de
awaitsequenciais → paralelize comPromise.all - Busca única grande → divida entre primeira visualização e adiado
- Perfil completo na navegação → use um helper de identidade de navegação leve
5. A navegação também deve parecer rápida
A velocidade percebida não se trata apenas do carregamento inicial. Transições de página, visitas repetidas e interações devem parecer igualmente rápidas:
- Pré-carregue rotas antes do usuário clicar
- Mostre
loading.tsximediatamente no início da navegação - Use SWR/cache para visitas de retorno instantâneas
Guia de Decisão
| Sintoma | Técnica |
|---|---|
| Tela em branco enquanto os dados carregam | Telas esqueleto / Suspense streaming |
| Clique em botão não tem resposta imediata | UI otimista / feedback instantâneo |
| Transição de página não começa visualmente após o clique | loading.tsx + pré-carregamento (prefetch) |
| Componente pesado atrasa a renderização inicial | Importação dinâmica |
| Spinner aparece em toda visita aos mesmos dados | SWR stale-while-revalidate |
| Uma API lenta bloqueia a página inteira | Suspense streaming (Server Components assíncronos paralelos) |
| Busca de autenticação/perfil atrasa a navegação ou a casca da página | Separação do layout de autenticação (veja references/auth-performance.md) |
| Imagens causam mudança de layout ou parecem lentas | next/image com priority / placeholder blur |
| Troca de fonte causa flash de texto não estilizado | next/font |
| Envio de formulário não tem feedback visual | useFormStatus / useActionState |
| Lista longa causa travamento de rolagem ou renderização lenta | Rolagem virtual (TanStack Virtual / react-window) |
| Atualização de estado não urgente bloqueia entrada do usuário | startTransition para renderização adiada |
Fluxo de Análise
Passo 1: Identifique o que os usuários veem primeiro
O que renderiza em 0–200ms? Se for uma tela em branco ou um spinner, mudanças estruturais são necessárias antes de correções de estilo.
Passo 2: Classifique os dados por urgência
- Primeira visualização — deve estar presente para a página fazer sentido
- Adiado — pode ser transmitido após a casca estar visível
- Sob demanda — carregue somente após interação do usuário
Passo 3: Corrija dependências de dados
awaitsequencial →Promise.allou Server Components assíncronos separados- Excesso de busca no carregamento inicial → divida a API ou pagine
- Perfil completo na navegação → helper de identidade de navegação separado
Passo 4: Aplique renderização progressiva
Escolha com base no que é necessário:
loading.tsx— esqueleto de navegação instantânea (página inteira)<Suspense>— streaming granular (por seção)useOptimistic— feedback instantâneo de mutaçãodynamic()— adie o bundle de componente pesado- SWR — visitas de retorno instantâneas a partir do cache
Passo 5: Valide a performance percebida
- Algo significativo aparece imediatamente (< 200ms)?
- O usuário é informado durante todos os estados de carregamento?
- A UI é responsiva antes que todos os dados cheguem?
- A interação produz feedback no primeiro quadro?
Formato de Saída
Ao aplicar esta skill, responda nesta estrutura:
## O que está acontecendo agora
- [descreva o que os usuários experimentam atualmente]
## Causa raiz
- [o que no código causa a sensação de lentidão]
## O que mudar e por quê
- [mudança 1]: [por que isso melhora a velocidade percebida]
- [mudança 2]: ...
## Implementação
[código]
## Notas
- [quaisquer ressalvas, casos de borda ou melhorias de acompanhamento]
Referências
Para detalhes de implementação com exemplos de código completos, veja:
references/nextjs-patterns.md— Telas esqueleto, UI otimista, Suspense streaming, pré-carregamento, importação dinâmica, SWR, imagens, fontes, feedback de interação,useFormStatus, rolagem virtual,startTransitionreferences/auth-performance.md— Separação de layout de autenticação, helpers de identidade de navegação, aquecimento de requisição/processo, design de estado de fallback
Leia a referência relevante ao implementar uma técnica específica.
Anti-Padrões a Corrigir
Sempre detecte e aborde:
- Aguardar perfil/autenticação completo no layout de nível superior → atrasa todas as páginas
- Cadeias de
awaitsequenciais para dados independentes → serialização desnecessária loading.tsxadicionado sem corrigir o excesso de busca subjacente- Renderizar conjuntos de dados inteiros no carregamento inicial → pagine ou virtualize
- Nenhum feedback visual ao clicar até que a operação seja concluída → reconheça imediatamente
- Fontes de autenticação diferentes na navegação vs. página → inconsistência de nome de usuário canônico
- Estado de envio de formulário gerenciado apenas após resposta do servidor → use
useFormStatuspara estado pendente instantâneo - Renderizar todos os itens de uma lista longa na montagem → virtualize (renderize apenas as linhas visíveis)
- Atualização de estado cara (filtro/ordenação/busca) bloqueando entrada do usuário → envolva em
startTransition