lenec ru

← все посты

INP вместо FID: что готовить

17K

В марте 2024 года Google заменил First Input Delay (FID) на Interaction to Next Paint (INP) в Core Web Vitals. К 2026 году все продукты, на которые я смотрю в работе, уже учитывают INP в SEO-стратегии. Расскажу, чем INP отличается от FID, как его измерять и какие техники реально помогают.

FID vs INP: ключевая разница

FID мерил задержку перед обработчиком первого взаимодействия. То есть «сколько прошло между кликом и началом выполнения JS». Если у тебя обработчик не блокировал главный поток — FID был отличный, даже если страница потом полминуты «думала» над результатом.

INP мерит общую задержку до следующего отрисованного кадра. То есть «сколько прошло между взаимодействием и тем, как пользователь увидел результат». Это включает и обработку, и обновление DOM, и рендер кадра.

Цели INP: ≤200 мс — хорошо, 200-500 мс — нужно улучшать, >500 мс — плохо.

В отличие от FID, INP смотрит на худший процент взаимодействий за всю сессию пользователя. То есть если 99% кликов отрабатывают за 50 мс, а один за 800 мс — твой INP будет ближе к 800 мс.

Как измерить

В реальной аудитории

Search Console → Core Web Vitals report показывает разбивку по INP. Это самый точный источник.

Через JavaScript на самом сайте:

import { onINP } from 'web-vitals';

onINP((metric) => {
  console.log('INP:', metric.value);
  // отправить в аналитику
  navigator.sendBeacon('/analytics', JSON.stringify(metric));
});

Метрика отправляется один раз на загрузку страницы. Если пользователь долго взаимодействует, обновление приходит позже.

В лаборатории

Lighthouse в DevTools показывает INP как часть Performance Insights. Но цифра там приблизительная — нужен реальный пользовательский ввод.

В Chrome DevTools на вкладке Performance можно записать сценарий взаимодействия и посмотреть детально, что блокирует кадр.

Что блокирует INP

Источники задержки:

  1. Обработчик события — если он делает много синхронной работы.
  2. Long tasks от других скриптов, которые блокируют главный поток в момент взаимодействия.
  3. Рендер кадра — если DOM-обновление вызывает дорогой layout/paint.

На моих проектах самые частые причины — третьи стороны (аналитика, баннеры) и тяжёлые React-компоненты, которые ререндерятся целиком.

Как уменьшить INP: тактики

1. Дробить тяжёлые задачи

Если обработчик клика делает 200 мс работы — это плохой INP. Разбей на части через scheduler.yield или requestIdleCallback:

async function applyFilter() {
  setLoading(true);
  await scheduler.yield(); // отдать управление браузеру
  const filtered = items.filter(predicate);
  await scheduler.yield();
  setItems(filtered);
  setLoading(false);
}

scheduler.yield() — нативный API в Chrome 129+, в других — через polyfill scheduler-polyfill. Помогает раздробить длинные задачи на короткие, не теряя последовательности.

2. Откладывать некритичную работу

Если после клика нужно отправить аналитику и обновить UI — UI важнее. Сначала покажи результат, потом отправляй:

function onClick() {
  setExpanded(true); // моментально
  requestIdleCallback(() => {
    track('expand', { id });
  });
}

requestIdleCallback запускает функцию когда браузер не занят. Аналитика прилетает позже, но пользователь уже видит результат.

3. Виртуализация списков

Если в списке 1000 элементов и клик на одном вызывает ререндер всего — это long task. Используй виртуализацию: TanStack Virtual или react-virtual. Рендерится только видимое окно, обновление быстрое.

4. Точечные ререндеры

В React: memo, useMemo, useCallback. В Zustand или Jotai — селекторы, чтобы компонент подписывался только на нужный кусок стейта.

На прошлом проекте у меня был INP 380 мс. Оказалось, при клике на один чекбокс ререндерился весь список из 800 элементов. После добавления memo и точечных селекторов — 90 мс.

5. Облегчить обработчики

Если в обработчике onChange input'а ты вызываешь синхронную валидацию через 500-строчную функцию — INP будет плохим. Замените на debounce + асинхронную валидацию через worker:

import { debounce } from 'lodash-es';

const validate = debounce(async (value: string) => {
  const result = await runWorkerValidation(value);
  setError(result);
}, 200);

6. Ленивая гидрация

На Astro — это client:idle или client:visible вместо client:load. На Next с RSC — серверные компоненты вместо клиентских везде, где можно.

Цель: меньше JS гидрируется при первом взаимодействии, меньше задержки.

Третьи стороны

Аналитика, чаты, A/B-инструменты — типичные виновники плохого INP. Они часто перехватывают события или делают синхронную работу.

Стратегия:

  • Загружать через partytown в отдельный worker.
  • Откладывать загрузку до первого взаимодействия (onClick на body).
  • Удалять то, чем команда реально не пользуется.

На одном проекте мы убрали Hotjar — INP упал на 60 мс. Команда полгода им не пользовалась.

React 19 и INP

React 19 принёс несколько улучшений, которые помогают:

  • useTransition — обновления, помеченные через transition, не блокируют срочные взаимодействия.
  • Улучшенный concurrent rendering — React сам разбивает рендер на части.
  • useDeferredValue — для значений, которые можно обновлять с задержкой.

Пример с useTransition:

const [isPending, startTransition] = useTransition();

function onSearch(query: string) {
  setQuery(query); // срочно — для input'а
  startTransition(() => {
    setResults(filterItems(query)); // не срочно — фон
  });
}

Браузер успевает отрисовать обновлённый input до того, как React закончит фильтрацию. INP остаётся хорошим даже на больших списках.

Что я бы делал перед релизом

  1. Прогнать сценарии типичных взаимодействий: клик меню, открытие модалки, скролл с фильтрами.
  2. Записать Performance в DevTools, посмотреть длинные задачи.
  3. Применить тактики выше.
  4. Проверить в реальной аудитории через 2-3 дня после релиза.

Грабли

Лабораторные замеры обманывают. На моей машине с 32 ГБ RAM и i9 INP всегда «отлично», даже на плохо оптимизированных страницах. Throttle в DevTools (Slow 4G + 4× CPU) приближает к реальности, но всё равно не точно.

Реальные данные приходят с задержкой. Search Console обновляет CrUX данные раз в день, и для исправления нужно подождать 28 дней (окно метрики). Не паникуй после релиза — измерения окрепнут к концу месяца.

Что копать дальше

INP — это про общее ощущение «снаппиности» приложения. Если у тебя в реальной аудитории INP 300+ мс, это не «надо ускорить кнопку». Это сигнал, что где-то в продукте лежит большая задача, которая блокирует UI на долгие сотни миллисекунд. Найти её — самое ценное, что ты сделаешь для пользователей в этом квартале.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.