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.
Nesta página 8 min de leitura
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
| Faixa | INP no percentil 75 |
|---|---|
| Bom | até 200 ms |
| Precisa melhorar | acima de 200 ms e até 500 ms |
| Ruim | acima 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
| Fase | O 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):
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.
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.
Use os dados de Long Animation Frames (LoAF) para chegar ao script responsável.
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:
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.
Clique em gravar, faça a interação e pare a gravação.
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.
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
sourceURLeinvokerda LoAF mostram quais são. Para cada um, avalie se ele traz valor que justifique o custo; se fica, carregue comasyncoudefer, 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: autoadia 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.
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.
Continue lendo
- SEO técnico
Robôs de IA no robots.txt: busca, acesso do usuário e treinamento
GPTBot, ClaudeBot, OAI-SearchBot, Google-Extended: o que cada robô de IA faz, se respeita o robots.txt e como bloquear o treinamento sem sair da busca.
- SEO técnico
Redirecionamentos: cadeias, loops e como corrigir
Como o Google trata 301, 302, 307 e 308, o que cadeias e loops causam no rastreamento e na velocidade, e como mapear, corrigir e testar redirecionamentos.
- SEO técnico
llms.txt: o que é, o que não faz e como criar
O llms.txt resume o site para agentes de IA. Veja o formato, o que Google e outras empresas dizem sobre ele, quando vale criar e como escrever um bom arquivo.