Como melhorar o INP: o que atrasa a resposta ao clique

O INP soma atraso de entrada, processamento e apresentação. Como achar a interação lenta e reduzir cada fase, com scheduler.yield e LoAF.

Checar meu site grátisVer os planos
Nesta página 8 min de leitura
  1. O que o INP mede
  2. As três fases de uma interação
  3. Tarefas longas: a origem da maioria dos problemas
  4. Como achar a interação lenta
  5. Como reduzir o atraso de entrada
  6. Como reduzir o tempo de processamento
  7. Como reduzir o atraso de apresentação
  8. Laboratório e campo dão respostas diferentes
  9. Como verificar o seu site

O INP (Interaction to Next Paint) mede quanto tempo a página leva para mostrar uma resposta visual depois de um clique, toque ou tecla. Ele observa as interações da visita inteira e reporta a mais lenta (em páginas com muitas interações, ignora a maior de cada 50). É bom até 200 ms e ruim acima de 500 ms, no percentil 75 das visitas (web.dev). Para reduzir, descubra qual interação é lenta, veja em qual das três fases o tempo está (espera, execução do código ou pintura) e corrija aquela fase.

Este texto segue os guias do web.dev sobre otimização de INP (atualizado em setembro de 2025) e de tarefas longas. Para o contexto das três Core Web Vitals, veja o guia de Core Web Vitals.

O que o INP mede

FaixaINP no percentil 75
Bomaté 200 ms
Precisa melhoraracima de 200 ms e até 500 ms
Ruimacima de 500 ms

A avaliação é feita separadamente para celular e desktop. A interação começa no gesto do usuário e termina quando o navegador pinta o próximo quadro na tela. Isso quer dizer que o INP não mede quando o seu código termina, e sim quando o usuário vê alguma coisa mudar. Quando a página volta do cache de voltar/avançar (bfcache), o INP recomeça do zero, porque para o usuário é uma visita nova (web.dev).

As três fases de uma interação

FaseO que éCausa comum
Atraso de entrada (input delay)Do gesto até o primeiro manipulador de evento começar a rodar.A thread principal estava ocupada com outra coisa: scripts carregando, timers, outra interação.
Tempo de processamento (processing duration)O tempo que os manipuladores de evento levam para rodar.Código demais dentro do click ou do input.
Atraso de apresentação (presentation delay)Do fim dos manipuladores até o quadro aparecer na tela.Recálculo de estilo, layout e pintura caros, DOM grande, muito HTML gerado por JavaScript.

As três fases são medidas somando todos os eventos do mesmo quadro (por exemplo pointerup, mouseup e click de um único clique), e é assim que a biblioteca web-vitals calcula (web.dev).

Tarefas longas: a origem da maioria dos problemas

O navegador executa quase tudo numa única thread principal: interpretar HTML e CSS, rodar JavaScript, calcular layout e pintar. Cada bloco de trabalho é uma tarefa. Uma tarefa acima de 50 ms é chamada de tarefa longa (web.dev). Enquanto ela roda, um clique fica na fila. Se a tarefa está rodando quando o usuário clica, aumenta o atraso de entrada. Se a tarefa é o próprio manipulador do clique, aumenta o tempo de processamento. O remédio nos dois casos é o mesmo: quebrar o trabalho em pedaços menores e devolver a vez ao navegador entre eles.

Como achar a interação lenta

Saber que o INP está alto não diz qual botão é o problema. O caminho que o web.dev recomenda vai do campo para o laboratório (web.dev):

  1. Confira o CrUX no PageSpeed Insights ou no Search Console. Ele diz se existe problema de INP no celular e no desktop, mas não diz qual interação causa.

  2. Colete dados de usuários reais com a biblioteca web-vitals na versão de atribuição. Ela informa o elemento clicado, o tipo de interação e o tempo de cada fase.

  3. Use os dados de Long Animation Frames (LoAF) para chegar ao script responsável.

  4. Reproduza a interação no Chrome DevTools para investigar e testar a correção.

Com a biblioteca web-vitals

import {onINP} from 'web-vitals/attribution';

onINP(({value, attribution}) => {
  console.log('INP', value);
  console.log('Elemento', attribution.interactionTarget);
  console.log('Tipo', attribution.interactionType); // 'pointer' ou 'keyboard'
  console.log('Atraso de entrada', attribution.inputDelay);
  console.log('Processamento', attribution.processingDuration);
  console.log('Apresentação', attribution.presentationDelay);
  console.log('Estado da página', attribution.loadState);
  console.log('Script mais longo', attribution.longestScript);
});

O campo loadState mostra se a interação aconteceu com a página ainda carregando, situação em que o atraso de entrada costuma ser maior. O longestScript traz o script mais longo que coincidiu com a interação e em qual fase ele rodou. Esses campos que dependem de LoAF só vêm preenchidos em navegadores que suportam a API (GitHub GoogleChrome/web-vitals).

Com a API Long Animation Frames

Um long animation frame é um quadro cuja atualização visual atrasou mais de 50 ms. Cada registro lista os scripts acima de 5 ms que rodaram no quadro, com URL do arquivo, nome da função, o que chamou o script (um listener de evento, um requestAnimationFrame, um timer) e quanto tempo foi gasto em layout forçado (Chrome for Developers). Pela URL do script dá para separar o seu código do código de terceiros.

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.duration, entry.blockingDuration, entry.scripts);
  }
});
observer.observe({type: 'long-animation-frame', buffered: true});

A LoAF existe no Chrome e no Edge desde a versão 123. Firefox e Safari não suportam (MDN browser-compat-data). Scripts de iframes de outra origem, workers e extensões não aparecem na atribuição.

Com o Chrome DevTools

O painel Performance tem uma visão de métricas ao vivo. Ao interagir com a página, ele registra o INP local e lista cada interação com o elemento, o tempo e as três fases (Chrome for Developers). Para investigar uma interação específica:

  1. Ative a limitação de CPU, ou use um Android de entrada ou intermediário com depuração remota. Máquinas rápidas escondem tarefas longas.

  2. Clique em gravar, faça a interação e pare a gravação.

  3. Na faixa Interactions, a interação aparece em cima da atividade da thread principal. A haste da esquerda é o atraso de entrada, o bloco sólido é o processamento e a haste da direita é a apresentação.

  4. Teste também durante o carregamento, logo que a página aparece, quando a thread principal costuma estar mais ocupada.

Esse roteiro é o do web.dev para diagnóstico manual em laboratório.

Como reduzir o atraso de entrada

O atraso de entrada é tempo em que o seu manipulador nem começou. A causa é outra coisa ocupando a thread principal no momento do clique:

  • Avaliação de scripts durante o carregamento. Interpretar, compilar e executar JavaScript gera tarefas longas justamente quando o usuário costuma interagir pela primeira vez. Menos JavaScript na carga inicial reduz esse risco.

  • Timers (setInterval, setTimeout) que rodam trabalho pesado em intervalos.

  • Interações sobrepostas: o processamento de uma tecla atrasa a seguinte.

  • Scripts de terceiros (chat, analytics, anúncios, testes A/B) que rodam na mesma thread. Os campos sourceURL e invoker da LoAF mostram quais são. Para cada um, avalie se ele traz valor que justifique o custo; se fica, carregue com async ou defer, ou mais tarde (web.dev).

Como reduzir o tempo de processamento

A regra do web.dev é fazer o mínimo possível dentro do manipulador de evento: atualizar a interface primeiro e adiar o resto.

Atualize a tela e adie o restante

campo.addEventListener('input', (evento) => {
  // O que o usuário precisa ver agora
  atualizarCampo(evento);

  // O resto fica para depois do próximo quadro
  requestAnimationFrame(() => {
    setTimeout(() => {
      contarPalavras();
      verificarOrtografia();
      salvarRascunho();
    }, 0);
  });
});

Esse padrão, adaptado do exemplo do web.dev, funciona em todos os navegadores. O requestAnimationFrame espera o próximo quadro e o setTimeout joga o trabalho para uma tarefa nova, depois da pintura.

Ceda a vez em trabalhos longos com scheduler.yield

Quando o trabalho é uma sequência de etapas, como processar uma lista, você pode devolver a vez ao navegador entre as etapas. O scheduler.yield() pausa a tarefa, deixa o navegador atender cliques e pintar, e retoma o seu código com prioridade sobre outras tarefas da fila (MDN). Segundo os dados de compatibilidade do MDN, ele existe no Chrome e no Edge 129 e no Firefox 142; o Safari ainda não suporta (MDN browser-compat-data, consultado em outubro de 2026). Por isso use com fallback:

function cederAVez() {
  if (globalThis.scheduler?.yield) {
    return scheduler.yield();
  }
  // Fallback para navegadores sem scheduler.yield
  return new Promise((resolve) => setTimeout(resolve, 0));
}

async function processarItens(itens) {
  for (const item of itens) {
    processar(item);
    await cederAVez();
  }
}

A função segue o modelo do guia de tarefas longas do web.dev. A diferença do fallback: com setTimeout a continuação vai para o fim da fila, e outros scripts (inclusive de terceiros) podem rodar antes dela. O código fica responsivo, só que sem a prioridade de retomada. Existe também o pacote scheduler-polyfill, citado no mesmo guia.

Evite layout forçado

Mudar um estilo e logo em seguida ler uma medida, como offsetHeight, obriga o navegador a recalcular o layout no meio do seu código. Feito dentro de um laço, isso multiplica o custo. Agrupe as leituras antes das escritas. Na LoAF, esse tempo aparece em forcedStyleAndLayoutDuration.

Como reduzir o atraso de apresentação

Depois que o seu código termina, o navegador ainda precisa recalcular estilos, fazer o layout e pintar. Esse custo cresce com o tamanho da página:

  • DOM grande. O Lighthouse avisa acima de 800 nós e marca como excessivo acima de 1.400 (web.dev). Remova invólucros desnecessários e insira conteúdo escondido (abas, menus) só quando o usuário pedir.

  • Conteúdo fora da tela renderizado à toa. A propriedade CSS content-visibility: auto adia a renderização de seções que não estão visíveis. Ela funciona no Chrome 85, no Firefox 125 e no Safari 18 em diante (MDN browser-compat-data).

  • Muito HTML gerado por JavaScript de uma vez. O navegador só pinta depois de interpretar e renderizar tudo. Gere em partes ou entregue pronto do servidor.

.secao-rodape {
  content-visibility: auto;
  contain-intrinsic-size: auto 600px; /* altura estimada, ajuste ao seu layout */
}

Laboratório e campo dão respostas diferentes

O Lighthouse não mede INP, porque não clica em nada. Ele reporta o TBT (Total Blocking Time), uma métrica de carregamento que se correlaciona com o INP mas não o substitui (web.dev). Uma nota boa no Lighthouse não garante INP bom, e interações depois do carregamento só aparecem se você testar o fluxo manualmente.

No campo também há diferenças de cobertura. O CrUX vem só de usuários do Chrome que aceitaram compartilhar estatísticas, no desktop e no Android (Chrome for Developers). A API de Event Timing, que a web-vitals usa para calcular o INP, existe no Chrome, no Firefox 89 e no Safari 26.2 em diante, mas a LoAF só no Chrome e no Edge. Na prática, o seu RUM mede o INP em mais navegadores, mas a atribuição detalhada por script vem só dos navegadores Chromium. A comparação entre essas fontes está em Lighthouse, CrUX e RUM.

Corrigir uma interação lenta costuma revelar a próxima. O web.dev trata a otimização de INP como um ciclo: medir no campo, reproduzir no laboratório, corrigir e medir de novo. Problemas de LCP e CLS têm guias próprios: como melhorar o LCP e como corrigir o CLS.

Como verificar o seu site

No WebVyze, o diagnóstico de INP usa o Google PageSpeed para mostrar a situação da página, e o teste de Core Web Vitals traz o INP do CrUX dos últimos 28 dias, no celular e no desktop, para até 10 URLs. As duas ferramentas são grátis. Nos planos pagos, um script de visitantes reais baseado na biblioteca web-vitals registra o INP de cada visita com a causa.

Perguntas frequentes

Rolar a página ou passar o mouse conta para o INP?
Não. O INP observa cliques, toques e teclas. Passar o mouse, rolar e dar zoom ficam de fora.
E se o visitante não clicar em nada?
Nesse caso a visita não tem valor de INP. Isso também vale para quem só rolou a página e para robôs que não simulam interação.
Interações dentro de iframes contam?
Contam para o INP do CrUX. Já as APIs do navegador, e por consequência a biblioteca web-vitals, não enxergam o conteúdo dos iframes, então o seu RUM pode mostrar um valor diferente do CrUX em páginas com iframes.
O isInputPending ainda serve para decidir quando ceder a vez?
O web.dev não recomenda mais essa API. Ela pode responder errado logo depois de uma interação e não cobre atualizações visuais que também precisam de espaço. A indicação atual é scheduler.yield ou scheduler.postTask.
Sobre o autor

Daniel Paz

Especialista em WordPress, performance e SEO, fundador da WebOption

Daniel Paz é especialista em WordPress, performance e SEO, fundador da WebOption e palestrante internacional. Com mais de 18 anos de experiência em desenvolvimento web, ajuda empresas a transformar sites em plataformas rápidas, eficientes e orientadas a resultados.

Veja o que o seu site precisa corrigir.

Checagem grátis por lei, com a causa e o guia de correção de cada problema.

Checar meu site grátisCriar conta grátis

Continue lendo

Parceiros

Benefícios de parceiros com o mesmo cupom.

WPLIGHT

Links de parceiros. Podemos receber uma comissão, sem custo adicional para você.