MohandOussadi
Retour au blog
Core Web Vitals : diagnostiquer et corriger un mauvais INP

Core Web Vitals : diagnostiquer et corriger un mauvais INP

20 janvier 2026
4 min de lecture
Performance
Core Web Vitals
React

Depuis mars 2024, l'INP (Interaction to Next Paint) a remplacé le FID parmi les Core Web Vitals, les trois indicateurs que Google utilise pour évaluer l'expérience utilisateur. C'est aussi celui que les applications React ont le plus de mal à respecter. Contrairement au FID, qui ne mesurait que le délai de la première interaction, l'INP observe toutes les interactions de la visite et retient l'une des plus lentes.

Les trois Core Web Vitals en bref

  • LCP (Largest Contentful Paint) : temps d'affichage du plus grand élément visible. Bon : 2,5 s ou moins.
  • CLS (Cumulative Layout Shift) : stabilité visuelle, les éléments qui « sautent ». Bon : 0,1 ou moins.
  • INP (Interaction to Next Paint) : délai entre une interaction (clic, touche, tap) et la prochaine mise à jour visuelle. Bon : 200 ms ou moins ; mauvais : plus de 500 ms.

Ces seuils s'évaluent au 75e percentile des visites réelles : il ne suffit pas que la page soit rapide sur votre MacBook, elle doit l'être sur le téléphone milieu de gamme de vos utilisateurs.

Décortiquer une interaction

L'INP d'une interaction se décompose en trois phases. Savoir laquelle domine indique directement quoi corriger.

Les trois phases de l'INP : délai d'entrée, traitement, délai de présentation
Les trois phases de l'INP : délai d'entrée, traitement, délai de présentation
  • Délai d'entrée : le fil principal est occupé par autre chose (un script tiers, une hydratation, un minuteur) quand l'utilisateur clique.
  • Durée de traitement : le temps d'exécution de vos gestionnaires d'événements, et dans React, du nouveau rendu qu'ils déclenchent.
  • Délai de présentation : le navigateur recalcule les styles, la mise en page et dessine. Un DOM énorme ou des animations coûteuses l'allongent.

Étape 1 : mesurer sur le terrain

Les outils de laboratoire (Lighthouse) ne simulent pas les vraies interactions. Il faut des données réelles : le rapport Core Web Vitals de Google Search Console, et surtout la librairie web-vitals dans sa version d'attribution, qui indique l'élément concerné et la répartition entre les trois phases.

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

onINP(({ value, rating, attribution }) => {
  sendToAnalytics({
    metric: "INP",
    value,
    rating,
    target: attribution.interactionTarget,
    inputDelay: attribution.inputDelay,
    processing: attribution.processingDuration,
    presentation: attribution.presentationDelay,
  });
});

Étape 2 : reproduire en local

Dans les DevTools de Chrome, l'onglet Performance affiche l'INP en direct pendant que vous interagissez. Activez un ralentissement du processeur (4x ou 6x) pour vous rapprocher d'un téléphone réel, enregistrez l'interaction coupable et cherchez les tâches longues (plus de 50 ms) marquées en rouge.

Étape 3 : corriger

Rendre la main au navigateur

Une tâche longue bloque tout. La découper permet au navigateur de dessiner le retour visuel entre deux morceaux de travail. Mettez d'abord à jour l'interface, puis faites le travail lourd ensuite.

async function onFilterChange(value: string) {
  setInputValue(value);          // retour visuel immédiat
  await yieldToMain();           // le navigateur peut dessiner
  runExpensiveFiltering(value);  // travail lourd ensuite
}

function yieldToMain() {
  // scheduler.yield() quand il est disponible, setTimeout sinon
  if ("scheduler" in globalThis && "yield" in (globalThis as any).scheduler) {
    return (globalThis as any).scheduler.yield();
  }
  return new Promise((resolve) => setTimeout(resolve, 0));
}

Dans React : les transitions

startTransition (ou useTransition) marque une mise à jour comme non urgente. React garde le champ de saisie réactif et interrompt le rendu coûteux si l'utilisateur continue de taper. useDeferredValue offre le même bénéfice pour une valeur dérivée.

const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);

return (
  <>
    <input value={query} onChange={(e) => setQuery(e.target.value)} />
    <ResultsList query={deferredQuery} />  {/* rendu non bloquant */}
  </>
);

Réduire le travail

  • Éviter les rendus inutiles : état placé trop haut dans l'arbre, contextes qui changent à chaque rendu. Le React Compiler et memo aident, à condition de mesurer.
  • Virtualiser les longues listes (TanStack Virtual) : afficher 30 lignes plutôt que 3 000.
  • Alléger le DOM : chaque nœud supplémentaire coûte au moment de la mise en page.
  • Auditer les scripts tiers : un outil d'analytics ou de chat peut à lui seul monopoliser le fil principal. Chargez-les après l'interaction ou dans un web worker.

Conclusion

Un mauvais INP n'est presque jamais causé par « React qui est lent », mais par une interaction précise qui fait trop de travail au mauvais moment. Mesurez sur le terrain, identifiez la phase dominante, puis donnez la priorité au retour visuel et repoussez le travail lourd. C'est souvent une poignée d'interactions à corriger pour faire passer tout un site au vert.

Partager cet article