Performance

INP: o que é, por que substituiu o FID e como melhorar

Entenda o Interaction to Next Paint (INP), por que ele substituiu o FID nas Core Web Vitals, como achar as interações lentas e o que corrigir no seu JavaScript.

Rafael Coelho

Por · Dev fullstack e criador do Analisa

Publicado em 8 min de leitura

Você clica no botão "Adicionar ao carrinho" e nada acontece por meio segundo. Clica de novo. Aí aparecem dois itens no carrinho. Essa sensação de site "travado" tem nome e número: INP, a métrica de responsividade das Core Web Vitals desde março de 2024.

Aqui eu explico o que o INP mede, por que ele substituiu o FID, como descobrir quais interações estão lentas no seu site e o que mudar no JavaScript para melhorar — com código.

O que é INP

INP (Interaction to Next Paint) mede quanto tempo a página leva para mostrar uma resposta visual depois que a pessoa interage com ela. "Interagir", aqui, é clicar, tocar na tela ou apertar uma tecla. Passar o mouse por cima e rolar a página não contam.

O relógio começa na interação e para quando o navegador pinta o próximo quadro na tela. Esse tempo tem três partes:

  1. Atraso de entrada (input delay): o tempo até o navegador conseguir começar a rodar os seus event listeners. Se a thread principal está ocupada com outra coisa, a interação espera na fila.
  2. Processamento (processing duration): o tempo rodando os handlers do evento — o seu código de onClick, onKeyDown etc.
  3. Atraso de apresentação (presentation delay): o tempo entre o fim dos handlers e o quadro aparecer: recalcular estilos, layout, pintar.

O INP observa todas as interações da visita, não só uma. O valor final é, na prática, a interação mais lenta; em páginas com muitas interações, a métrica descarta a pior a cada 50 para que um caso isolado não domine o resultado. Se a pessoa não interagiu com a página, não existe INP para aquela visita.

Qual é um INP bom

FaixaINP
Bomaté 200 ms
Precisa melhorarde 200 ms a 500 ms
Ruimacima de 500 ms

O valor que conta é o percentil 75 das visitas reais, separando celular e desktop. Ou seja: para passar, três em cada quatro visitas precisam ter INP de até 200 ms. A média não serve — ela esconde justamente a parte do público com celular mais fraco, que é quem mais sofre.

Por que o INP substituiu o FID

O FID (First Input Delay) media só uma coisa: o atraso de entrada da primeira interação. Ele ignorava quanto tempo o seu código levava para processar o clique, ignorava a pintura e ignorava tudo o que acontecia depois da primeira interação. Um site podia ter FID ótimo e um filtro de busca que travava a tela por um segundo.

O INP cobre o ciclo inteiro (entrada, processamento e apresentação) em todas as interações. Por isso o Google trocou: em 12 de março de 2024 o INP virou Core Web Vital no lugar do FID. Em setembro de 2024 as ferramentas do Chrome, como o CrUX e o PageSpeed Insights, pararam de reportar o FID.

Campo e laboratório: por que o seu Lighthouse não mostra INP

O INP depende de interação real. Um teste automatizado que só carrega a página não clica em nada, então não tem como medir o INP. Por isso vale separar as duas fontes de dados:

  • Dados de campo (field): visitas reais. É o que o Google usa para avaliar as Core Web Vitals do seu site. Vêm do CrUX (Chrome User Experience Report, com os últimos 28 dias de usuários do Chrome) ou de uma ferramenta de RUM instalada no seu site.
  • Dados de laboratório (lab): um teste controlado, como o Lighthouse. Num relatório comum do Lighthouse, o mais perto do INP é o TBT (Total Blocking Time): quanto a thread principal ficou bloqueada durante o carregamento. TBT alto costuma andar junto com INP ruim, porque tarefas longas são a causa número um de atraso de entrada.

O verificador de Web Vitals mostra os dois para qualquer página pública: o INP de campo do CrUX (da URL ou, sem amostra suficiente, da origem inteira) e a nota do Lighthouse com o TBT, no celular ou no desktop.

O CrUX só tem dados de sites com tráfego suficiente vindo do Chrome. Para sites menores, ou para ver página por página, você precisa medir no próprio site. O SDK do Analisa coleta Web Vitals automaticamente (LCP, FCP, CLS, INP e TTFB), e o relatório de Web Vitals do painel (em /reports/web-vitals) mostra o p75 de cada métrica, a faixa (bom, precisa melhorar, ruim), o número de amostras e as páginas com pior desempenho.

Como encontrar as interações lentas

1. Descubra quais páginas sofrem

Comece pelos dados de campo: quais páginas têm INP acima de 200 ms no p75? Normalmente são poucas, e quase sempre as mais interativas — busca com filtro, carrinho, formulário longo, painel.

2. Descubra qual interação

Saber que a página de busca tem INP de 450 ms não diz qual clique é o problema. O build de atribuição da biblioteca web-vitals, do time do Chrome, diz qual elemento recebeu a interação e onde o tempo foi gasto. Dá para mandar isso como evento para o Analisa e ver no relatório de eventos:

Atribuição do INP em produçãojs
import { onINP } from 'web-vitals/attribution'

onINP(({ value, rating, attribution }) => {
  if (rating === 'good') return // só interessa o que está lento
  window.analytics.track('inp_lento', {
    valor_ms: Math.round(value),
    elemento: attribution.interactionTarget, // seletor CSS do elemento
    tipo: attribution.interactionType, // 'pointer' ou 'keyboard'
    atraso_entrada_ms: Math.round(attribution.inputDelay),
    processamento_ms: Math.round(attribution.processingDuration),
    apresentacao_ms: Math.round(attribution.presentationDelay),
  })
})

A parte que domina diz onde mexer: atraso de entrada alto aponta para outras tarefas ocupando a thread (scripts de terceiros, hidratação, timers); processamento alto aponta para o seu handler; apresentação alta aponta para DOM grande ou renderização cara.

3. Reproduza no laboratório

Com a interação identificada, abra o Chrome DevTools, aba Performance:

  1. Ligue o CPU throttling (4x ou 6x) para simular um celular comum. O seu notebook é rápido demais para reproduzir o problema.
  2. Grave, faça a interação lenta e pare a gravação.
  3. Procure a interação na trilha Interactions e as tarefas longas (marcadas com um triângulo vermelho) na thread principal. O gráfico de chamas mostra qual função gastou o tempo.

Nas versões recentes do Chrome, o próprio painel Performance mostra o INP local em tempo real enquanto você clica pela página, sem precisar gravar.

Como melhorar o INP

1. Quebre tarefas longas e devolva a vez para o navegador

Qualquer tarefa de JavaScript com mais de 50 ms é considerada longa. Enquanto ela roda, o navegador não consegue responder a nada. A solução é dividir o trabalho e, entre um pedaço e outro, ceder a thread principal (yield) para o navegador processar interações e pintar.

yieldToMain: ceder a thread entre pedaços de trabalhojs
function yieldToMain() {
  // scheduler.yield() continua o trabalho com prioridade; fallback para setTimeout
  if (globalThis.scheduler?.yield) return scheduler.yield()
  return new Promise((resolve) => setTimeout(resolve, 0))
}

async function processarItens(itens) {
  let inicio = performance.now()
  for (const item of itens) {
    processar(item)
    // cede só quando já passou ~50 ms, para não pagar o custo a cada item
    if (performance.now() - inicio > 50) {
      await yieldToMain()
      inicio = performance.now()
    }
  }
}

O scheduler.yield() existe no Chrome e no Edge desde a versão 129 e no Firefox desde a 142; o Safari ainda não tem, por isso o fallback com setTimeout.

2. Mostre a resposta primeiro, faça o resto depois

O INP para no próximo quadro pintado. Então a pergunta é: o que precisa estar na tela agora? Atualize a interface (botão em estado de carregando, item marcado) e empurre o resto — salvar, sincronizar, mandar analytics — para depois de ceder a thread:

Feedback imediato, trabalho pesado depoisjs
botao.addEventListener('click', async () => {
  botao.disabled = true
  botao.textContent = 'Adicionando…' // isso precisa aparecer já

  await yieldToMain() // deixa o navegador pintar o quadro

  atualizarCarrinho() // trabalho mais pesado
  enviarEventos() // e o que não é urgente
})

Se você usa React, o useTransition faz algo parecido para atualizações de estado: marca a atualização cara como não urgente, e o React mantém a interface respondendo enquanto calcula.

3. Debounce no que dispara a cada tecla

Busca com filtro que recalcula a lista inteira a cada letra digitada é uma fábrica de INP ruim. Atualize o campo na hora e espere a pessoa parar de digitar para fazer o trabalho:

Debounce de 200 ms na buscajs
function debounce(fn, ms) {
  let t
  return (...args) => {
    clearTimeout(t)
    t = setTimeout(() => fn(...args), ms)
  }
}

const filtrar = debounce((termo) => renderizarResultados(buscar(termo)), 200)
campoBusca.addEventListener('input', (e) => filtrar(e.target.value))

4. Evite layout thrashing

Ler uma medida de layout (offsetHeight, getBoundingClientRect()) logo depois de alterar um estilo obriga o navegador a recalcular o layout na hora, de forma síncrona. Num laço, isso multiplica o custo:

Antes e depoisjs
// Ruim: lê e escreve intercalado — um layout forçado por item
for (const card of cards) {
  card.style.height = `${card.firstElementChild.offsetHeight}px`
}

// Melhor: lê tudo primeiro, escreve tudo depois — um layout só
const alturas = cards.map((card) => card.firstElementChild.offsetHeight)
cards.forEach((card, i) => {
  card.style.height = `${alturas[i]}px`
})

5. Mande menos JavaScript

Menos código é menos tarefa longa, principalmente durante o carregamento, quando a pessoa já está tentando clicar:

  • Corte scripts de terceiros que você não usa mais (chat, mapas de calor, pixels esquecidos). Cada um disputa a mesma thread principal.
  • Divida o bundle e carregue sob demanda o que não aparece na primeira tela (modais, editores, gráficos).
  • Cuidado com a hidratação em frameworks com SSR: a página aparece pronta, mas não responde até o JavaScript terminar de hidratar. Menos componentes interativos na primeira dobra ajudam.

6. Reduza o trabalho de renderização

Se o atraso de apresentação domina, o problema é o tamanho do que o navegador precisa recalcular. DOM com milhares de nós, listas enormes renderizadas de uma vez e CSS muito complexo pesam a cada interação. Virtualize listas longas e use content-visibility: auto em seções fora da tela para o navegador pular o trabalho de renderização delas até que fiquem visíveis.

Checklist

  1. Veja o INP de campo das suas páginas principais (CrUX no verificador de Web Vitals ou o relatório Web Vitals do Analisa).
  2. Nas páginas acima de 200 ms, use a atribuição do web-vitals para achar a interação e a parte (entrada, processamento ou apresentação) que domina.
  3. Reproduza no DevTools com CPU throttling.
  4. Quebre tarefas longas com yieldToMain, dê feedback antes do trabalho pesado, use debounce em input.
  5. Separe leituras e escritas de layout.
  6. Corte JavaScript e terceiros que não se pagam.
  7. Meça de novo depois do deploy — o CrUX leva até 28 dias para refletir a mudança por completo; o RUM no próprio site mostra antes.

Se você ainda não mede Web Vitals de usuários reais, o Analisa coleta LCP, INP, CLS, FCP e TTFB automaticamente assim que o script é instalado — o guia de primeiros passos mostra como, e há guias para Next.js e outras stacks.

Fontes

  1. Interaction to Next Paint (INP) — web.dev
  2. Interaction to Next Paint becomes a Core Web Vital on March 12 — web.dev
  3. Optimize Interaction to Next Paint — web.dev
  4. Optimize long tasks — web.dev
  5. Use scheduler.yield() to break up long tasks — Chrome for Developers
  6. Find slow interactions in the field — web.dev
  7. First Input Delay (FID) — web.dev
  8. web-vitals — upgrading to v4 (atribuição do INP) — GoogleChrome/web-vitals (GitHub)
  9. Scheduler: yield() method — MDN Web Docs

Meça de onde vêm seus clientes — sem cookies

O Analisa mostra de onde vem cada visita (referrer e UTM), quantas viram conversão e onde o funil perde gente, com IP anonimizado na entrada. Grátis até 10 mil eventos por mês.

Ferramentas e páginas relacionadas

Rafael Coelho

Sobre o autor

Rafael Coelho

Dev fullstack e criador do Analisa. Escreve sobre programação, produto e dados — e mede tudo com o próprio Analisa.

Ver todos os artigos

Leia também

INP: o que é, por que substituiu o FID e como melhorar · Analisa