Interaction to Next Paint (INP): O Guia Definitivo de Otimização (2025)
Em março de 2024, o Google substituiu oficialmente o First Input Delay (FID) pelo Interaction to Next Paint (INP) como uma Core Web Vital. Isso não foi apenas uma troca menor de métricas; foi uma mudança fundamental na forma como medimos a responsividade da web.
Por anos, desenvolvedores otimizaram para o primeiro clique. Mas os usuários não clicam apenas uma vez. Eles digitam, alternam, arrastam e esperam que a interface responda instantaneamente todas as vezes. O FID media apenas a primeira impressão. O INP mede todo o relacionamento.
Se você notou que seu "Total Blocking Time" (TBT) está alto, ou sua pontuação no Lighthouse é verde, mas seus usuários reais reclamam de uma UI "travada", este guia é para você. Vamos mergulhar fundo na mecânica da thread principal do navegador para entender por que as interações atrasam e como corrigir isso.
A Anatomia de uma Interação
Para otimizar o INP, você deve entender o que acontece quando um usuário clica em um botão. Não é instantâneo. O navegador passa por três fases distintas:
- Input Delay (Atraso de Entrada): O tempo desde o toque do usuário até o navegador começar a executar o manipulador de eventos. Muitas vezes causado por outras tarefas bloqueando a thread principal (ex: um processo de hydration rodando exatamente quando o usuário clica).
- Processing Time (Tempo de Processamento): O tempo que leva para rodar seus callbacks de eventos (atualizações de estado do React, manipulação do DOM, lógica complexa).
- Presentation Delay (Atraso de Apresentação): O tempo desde que seu código termina de rodar até o navegador realmente pintar o próximo frame na tela.
INP = Input Delay + Processing Time + Presentation Delay
A maioria dos desenvolvedores foca apenas no passo 2 (otimizar seu código). No entanto, uma grande parte das pontuações ruins de INP vem dos passos 1 e 3.
Por que console.time() Está Mentindo para Você
Você pode envolver seu manipulador de clique em um console.time() e ver que ele roda em 20ms. "Ótimo!", você pensa, "Meu código é rápido." Mas sua pontuação INP reporta 400ms. Por quê?
Porque console.time() mede apenas a execução do JavaScript. Ele não mede o custo de renderização.
Se sua atualização de estado React de 20ms desencadeia uma re-renderização de toda a árvore de componentes, forçando o navegador a recalcular estilos e layout para 5.000 nós DOM, esse "layout thrashing" acontece depois que seu JavaScript termina, mas antes da próxima pintura. Isso é Presentation Delay, e conta contra seu INP.
Estratégia 1: Cedendo à Thread Principal (Yielding)
A regra de ouro da responsividade é: Não monopolize a thread principal.
Se você tem uma tarefa de longa duração (ex: processar um grande array de dados), o navegador fica "congelado". Ele não pode lidar com novas entradas ou pintar a tela. A solução é dividir tarefas longas em pedaços menores e "ceder" (yield) o controle de volta ao navegador entre eles.
O Jeito Antigo: setTimeout
function processData(items) { if (items.length === 0) return; // Processar um item doHeavyWork(items[0]); // Agendar o resto para depois setTimeout(() => { processData(items.slice(1)); }, 0); }
Isso funciona, mas setTimeout tem um atraso mínimo (frequentemente 4ms ou mais) e não prioriza tarefas de forma inteligente.
O Jeito Moderno: scheduler.yield()
A nova API scheduler.yield() (atualmente em origin trial ou polyfilled) permite que você ceda à thread principal e retome a execução imediatamente sem a penalidade do setTimeout.
async function processData(items) { for (const item of items) { doHeavyWork(item); // Ceder à thread principal para deixar o navegador lidar com inputs/pintura // Isso verifica se há input de usuário pendente! await scheduler.yield(); } }
Isso muda o jogo. Diz ao navegador: "Vou pausar por um microssegundo. Se tiver um clique esperando, lide com ele agora. Se não, continuarei."
Estratégia 2: Otimizando o Presentation Delay
Se seu JavaScript é rápido, mas a pintura é lenta, você tem um problema de renderização.
- Reduzir o Tamanho do DOM: Uma árvore DOM massiva aumenta o custo de Recálculo de Estilo e Layout. Virtualize listas longas.
- CSS Containment: Use a propriedade
content-visibility: autopara pular a renderização de conteúdo fora da tela. - Evitar Layout Thrashing: Não leia propriedades de layout (como
offsetHeight) imediatamente após escrevê-las. Isso força o navegador a realizar um cálculo de layout síncrono.
Estratégia 3: Feedback Imediato (UI Otimista)
Psicologicamente, um usuário sente que um app é "lento" se não vê uma reação. Tecnicamente, o INP mede o tempo até a próxima pintura.
Se você tem uma operação pesada (como uma chamada de API), pinte algo imediatamente.
Padrão Ruim:
button.addEventListener('click', async () => { const data = await fetchData(); // Espera pela rede... renderData(data); // ...então pinta. });
O INP inclui o tempo de espera da rede!
Padrão Bom:
button.addEventListener('click', () => { showSpinner(); // Pinta imediatamente! O INP para de medir aqui. fetchData().then(data => { renderData(data); hideSpinner(); }); });
Ao mostrar um spinner ou um estado ativo imediatamente, você "completa" a interação da perspectiva do navegador. A requisição de rede subsequente e a renderização final são tarefas separadas que não prejudicam sua pontuação INP.
Conclusão
O INP é uma métrica rigorosa, mas nos força a construir software melhor. Ele nos empurra para longe de arquiteturas "pesadas em JS, bloqueadoras de thread principal" em direção a designs "assíncronos, que cedem controle e são responsivos".
Comece perfilando suas interações mais lentas. É a lógica? A renderização? Ou apenas uma thread principal ocupada? Uma vez que você identifique o gargalo, as ferramentas—scheduler.yield(), virtualização de DOM e atualizações de UI otimistas—estão prontas para você usar.
Faça seu site parecer instantâneo. Seus usuários (e seus rankings de SEO) agradecerão.
Explore ferramentas relacionadas
Experimente estas ferramentas gratuitas do Pockit