Core Web Vitals Next.js en pratique : guide complet LCP/FCP/CLS

Le mois dernier, j’ai repris l’optimisation performance d’un projet e-commerce : le score Lighthouse stagnait autour de 65, le LCP (Largest Contentful Paint) oscillait autour de 3,5 secondes. Le taux de churn était élevé et la conversion ne décollait pas.
Deux semaines d’optimisation systématique des Core Web Vitals plus tard : Lighthouse à 95, LCP à 1,2 s. Et surtout, +28 % de conversion. L’optimisation performance n’est pas qu’une question de métriques — c’est de la valeur business concrète.
Selon les données 2025, seulement 47 % des sites atteignent le standard Core Web Vitals de Google. Une mauvaise performance peut coûter 8 à 35 % de revenus, de classement et de conversions. Ce n’est pas de l’alarmisme — c’est le prix réel en business.
Dans cet article, je partage mon expérience terrain sur l’optimisation Next.js. Vous y trouverez :
- Les méthodes concrètes pour optimiser les 3 métriques clés (LCP, INP, CLS)
- Plus de 10 exemples de code prêts à copier
- 5 pièges de performance les plus fréquents
Core Web Vitals : ce qui change en 2025
Avant d’optimiser, clarifions les règles du jeu. Beaucoup d’articles parlent encore du FID (First Input Delay), alors que cette métrique a été retirée en mars 2024.
Les trois métriques clés actuelles
Google se concentre désormais sur ces trois indicateurs :
-
LCP (Largest Contentful Paint) — plus grand élément de contenu visible
- Objectif : ≤ 2,5 s
- Mesure la performance de chargement
- Pèse 25 % dans le score Lighthouse Performance
-
INP (Interaction to Next Paint) — interaction jusqu’au prochain rendu
- Objectif : ≤ 200 ms
- Mesure la réactivité aux interactions
- Nouvelle métrique depuis mars 2024, remplace le FID
-
CLS (Cumulative Layout Shift) — décalage cumulé de mise en page
- Objectif : < 0,1
- Mesure la stabilité visuelle
Le FCP (First Contentful Paint) n’est plus une métrique Core Web Vitals, mais reste important pour la première impression utilisateur.
Pourquoi s’y intéresser ?
Vous connaissez sans doute ce scénario : vous ouvrez un site, vous visez un bouton, la page saute et vous cliquez ailleurs. C’est un mauvais CLS.
Ou vous attendez plusieurs secondes devant un écran blanc en vous demandant si la connexion est coupée. C’est un LCP trop lent.
D’après l’équipe Chrome, le LCP moyen des meilleurs sites tourne autour de 1 220 ms. Si votre LCP dépasse 2,5 s, vous êtes derrière la plupart de vos concurrents.
Optimisation LCP — afficher rapidement le plus grand élément
Le LCP a été la métrique sur laquelle j’ai le plus travaillé — et celle où les gains sont les plus visibles. Voici comment procéder.
Étape 1 : identifier votre élément LCP
Avant d’optimiser, il faut savoir quel élément est le LCP. En général :
- L’image hero above-the-fold
- La vignette d’une vidéo
- Un bloc de titre ou de texte volumineux
Méthode rapide :
- Ouvrir Chrome DevTools (F12)
Ctrl+Shift+P(Mac :Cmd+Shift+P)- Taper « Show Rendering »
- Cocher « Core Web Vitals »
- Rafraîchir — l’élément LCP s’affiche en haut à droite
Sur le projet e-commerce, l’élément LCP était l’image principale de la page d’accueil : un JPG de 2 Mo. Le problème était là.
Optimisation images : bien utiliser next/image
Beaucoup pensent qu’utiliser le composant Image de Next.js suffit. Ce n’est pas le cas — j’ai fait cette erreur au début, et le LCP restait lent.
La priorité est la clé
Pour l’image LCP, il faut dire explicitement au navigateur : « Cette image est importante, chargez-la en priorité ! » Next.js propose deux approches :
Next.js 13-15 (versions courantes) :
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/hero-image.jpg"
width={1200}
height={630}
priority // Clé ! Priorité de chargement
fetchPriority="high" // Double sécurité
alt="Visuel produit principal"
/>
);
}
Next.js 16+ (dernière version) :
Next.js 16 a déprécié priority au profit de :
<Image
src="/hero-image.jpg"
width={1200}
height={630}
loading="eager" // Chargement immédiat, pas de lazy load
fetchPriority="high" // Haute priorité
alt="Visuel produit principal"
/>
Pourquoi deux attributs ? priority ajoute automatiquement une balise preload ; fetchPriority indique l’importance de la ressource. Ensemble, ils fonctionnent mieux.
À comparer avec les mauvaises pratiques :
// ❌ Erreur : lazy load par défaut, LCP lent
<Image
src="/hero-image.jpg"
width={1200}
height={630}
alt="Visuel produit principal"
/>
// ❌ Erreur : dimensions manquantes
<Image
src="/hero-image.jpg"
priority
alt="Visuel produit principal"
/>
L’importance des dimensions
Le composant Image exige width et height — ce n’est pas une contrainte arbitraire, mais une protection contre le CLS.
Pour un layout responsive :
// Utiliser fill pour le responsive
<div style={{ position: 'relative', width: '100%', aspectRatio: '16/9' }}>
<Image
src="/hero-image.jpg"
fill
priority
style={{ objectFit: 'cover' }}
alt="Visuel produit principal"
/>
</div>
Le conteneur parent doit avoir une taille explicite ou un aspect-ratio, sinon le navigateur ne sait pas quelle surface allouer.
Optimisation polices : next/font en inline automatique
Un chargement de polices lent pénalise aussi le LCP — surtout les polices CJK qui pèsent plusieurs Mo. Next.js 13 a introduit next/font pour automatiser l’optimisation.
Google Fonts :
// app/layout.jsx
import { Inter, Noto_Sans_SC } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Évite le flash de texte
});
const notoSansSC = Noto_Sans_SC({
subsets: ['chinese-simplified'],
weight: ['400', '700'],
display: 'swap',
});
export default function RootLayout({ children }) {
return (
<html lang="zh-CN" className={`${inter.className} ${notoSansSC.className}`}>
<body>{children}</body>
</html>
);
}
Polices personnalisées :
import localFont from 'next/font/local';
const myFont = localFont({
src: './my-font.woff2',
display: 'swap',
});
export default function Layout({ children }) {
return <div className={myFont.className}>{children}</div>;
}
next/font optimise automatiquement :
- Inline du CSS de polices, moins de requêtes réseau
- Sous-ensemble de caractères, charge uniquement le nécessaire
- Preload des fichiers de polices
- Suppression des décalages de mise en page
Après migration vers next/font, le LCP a encore baissé de 0,3 s.
Réduire le temps de réponse serveur
Images et polices optimisées, mais LCP encore lent ? Le TTFB (Time To First Byte) est peut-être en cause.
Privilégier la génération statique
Next.js propose plusieurs modes de rendu, du plus rapide au plus lent :
- SSG (Static Site Generation) — HTML généré au build, le plus rapide
- ISR (Incremental Static Regeneration) — régénération statique à la demande
- SSR (Server-Side Rendering) — HTML généré à chaque requête, plus lent
- CSR (Client-Side Rendering) — rendu côté client, LCP le plus lent
Utilisez SSG dès que possible :
// app/blog/[slug]/page.jsx
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug);
return <article>{/* ... */}</article>;
}
Si les données doivent rester à jour, utilisez ISR :
// app/products/[id]/page.jsx
export const revalidate = 3600; // Régénération toutes les heures
export default async function ProductPage({ params }) {
const product = await getProduct(params.id);
return <div>{/* ... */}</div>;
}
CDN et Edge Network
Sur Vercel, les ressources statiques sont distribuées sur le Edge Network mondial, ce qui réduit fortement le TTFB.
Sur d’autres plateformes, combinez avec Cloudflare, AWS CloudFront ou un CDN équivalent.
Éviter les calculs lourds côté serveur
J’ai aussi tombé dans ce piège : un traitement de données complexe côté serveur prenait 500 ms à chaque rendu, plombant le LCP.
Contre-exemple :
// ❌ Erreur : calcul à chaque requête
export default async function Page() {
const data = await fetchData();
const processed = heavyProcessing(data); // 500 ms
return <div>{processed}</div>;
}
Bonne approche :
Mettre en cache le résultat ou calculer au build :
// ✅ Correct : calcul au build
export async function generateStaticParams() {
const data = await fetchData();
const processed = heavyProcessing(data);
await saveProcessedData(processed);
}
export default async function Page() {
const processed = await getProcessedData(); // Lecture directe
return <div>{processed}</div>;
}
Optimisation CLS — éliminer les sauts de mise en page
Le CLS (Cumulative Layout Shift) est la métrique la plus frustrante. La page saute soudainement — mauvaise expérience. Ce problème m’a occupé un bon moment.
Réserver les dimensions images et médias
La cause la plus fréquente : une image qui charge et modifie la mise en page. Solution : indiquer les dimensions à l’avance.
Le composant Image Next.js gère cela :
// ✅ Correct : dimensions explicites, pas de CLS
<Image
src="/product.jpg"
width={400}
height={300}
alt="Photo produit"
/>
Et pour les images dynamiques ?
Depuis un CMS, sans connaître les dimensions :
// ✅ Correct : aspect-ratio pour réserver l'espace
<div style={{ position: 'relative', width: '100%', aspectRatio: '4/3' }}>
<Image
src={dynamicImageUrl}
fill
style={{ objectFit: 'cover' }}
alt="Image dynamique"
/>
</div>
Même logique pour la vidéo :
<video
width="1280"
height="720"
poster="/video-poster.jpg"
controls
>
<source src="/video.mp4" type="video/mp4" />
</video>
Espace réservé pour le contenu dynamique
Publicités, bandeaux de notification, embeds (cartes Twitter…) provoquent du CLS. Réservez l’espace à l’avance.
Conteneur publicitaire :
// ✅ Correct : hauteur réservée
<div
style={{
minHeight: '250px', // Hauteur standard Google AdSense
backgroundColor: '#f0f0f0' // Couleur de placeholder
}}
>
{/* Script publicitaire */}
<ins className="adsbygoogle" />
</div>
Bandeau de notification :
Ne laissez pas un bandeau apparaître brusquement :
// ❌ Erreur : apparition soudaine, page qui descend
{showBanner && <NotificationBanner />}
// ✅ Correct : espace réservé
<div style={{ minHeight: '60px' }}>
{showBanner ? <NotificationBanner /> : <div style={{ height: '60px' }} />}
</div>
Skeleton screens :
Pour le contenu chargé dynamiquement, utilisez des skeletons :
function ProductList() {
const { data, isLoading } = useQuery('products', fetchProducts);
if (isLoading) {
return (
<div className="grid grid-cols-3 gap-4">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="skeleton" style={{ height: '300px' }} />
))}
</div>
);
}
return (
<div className="grid grid-cols-3 gap-4">
{data.map(product => <ProductCard key={product.id} {...product} />)}
</div>
);
}
Optimisation du chargement des polices
Le chargement de polices provoque aussi du CLS — flash ou changement de police visible.
next/font gère cela automatiquement, mais vous pouvez affiner :
const inter = Inter({
subsets: ['latin'],
display: 'optional', // Si la police n'est pas prête, utiliser la police système
adjustFontFallback: true, // Ajuster la taille du fallback pour limiter le saut
});
Options pour display :
swap: fallback d’abord, puis bascule (peut causer du CLS)optional: si la police n’est pas prête à temps, garder le fallback (recommandé)block: texte masqué brièvement en attendant la police (déconseillé)fallback: compromis entre swap et optional
Je recommande optional : la police custom peut ne pas s’afficher, mais l’expérience reste stable.
Éviter le responsive piloté par JavaScript
Piège CLS le plus courant en 2025 ! Beaucoup de projets Next.js commettent cette erreur.
Code typiquement problématique :
// ❌ Erreur : CLS important
import { useMediaQuery } from '@/hooks/useMediaQuery';
export default function ResponsiveLayout() {
const isMobile = useMediaQuery('(max-width: 768px)');
return (
<div>
{isMobile ? (
<MobileNav />
) : (
<DesktopNav />
)}
</div>
);
}
Pourquoi du CLS ?
- Au premier rendu, le JavaScript n’a pas encore tourné —
isMobilevautundefinedou une valeur par défaut - Après exécution,
useMediaQueryretourne la vraie valeur - Re-render, mise en page qui change brusquement
En SSR, c’est pire : le serveur ne connaît pas la largeur d’écran du client.
Bonne approche : media queries CSS
// ✅ Correct : affichage/masquage en CSS, pas de CLS
export default function ResponsiveLayout() {
return (
<>
<nav className="mobile-nav md:hidden">
<MobileNav />
</nav>
<nav className="desktop-nav hidden md:block">
<DesktopNav />
</nav>
</>
);
}
Ou avec CSS-in-JS :
export default function ResponsiveLayout() {
return (
<div className="responsive-container">
<MobileNav />
<DesktopNav />
<style jsx>{`
.responsive-container > :global(.mobile-nav) {
display: block;
}
.responsive-container > :global(.desktop-nav) {
display: none;
}
@media (min-width: 768px) {
.responsive-container > :global(.mobile-nav) {
display: none;
}
.responsive-container > :global(.desktop-nav) {
display: block;
}
}
`}</style>
</div>
);
}
Si vous devez vraiment distinguer côté JS (requêtes différentes selon l’appareil), récupérez le User-Agent côté serveur :
// app/page.jsx
import { headers } from 'next/headers';
export default async function Page() {
const headersList = headers();
const userAgent = headersList.get('user-agent') || '';
const isMobile = /mobile/i.test(userAgent);
return (
<div>
{isMobile ? <MobileView /> : <DesktopView />}
</div>
);
}
Serveur et client rendent la même chose — pas de CLS.
Optimisation FCP — accélérer le premier contenu visible
Le FCP n’est pas une métrique Core Web Vitals, mais il façonne la première impression. Un écran blanc trop long, et l’utilisateur ferme l’onglet.
Optimiser les ressources critiques
Un FCP lent vient souvent de ressources bloquant le rendu. Ouvrez le panneau Coverage de Chrome DevTools pour repérer CSS et JavaScript inutilisés.
Inline du CSS critique :
Next.js optimise le CSS automatiquement, mais vous pouvez inline les styles essentiels :
// app/layout.jsx
export default function RootLayout({ children }) {
return (
<html>
<head>
<style
dangerouslySetInnerHTML={{
__html: `
/* CSS critique : styles indispensables above-the-fold */
body { margin: 0; font-family: system-ui; }
.hero { height: 100vh; }
`,
}}
/>
</head>
<body>{children}</body>
</html>
);
}
Différer le CSS non critique :
Pour les styles hors first screen (modales, sections repliables) :
// Import dynamique du CSS dans le composant
import dynamic from 'next/dynamic';
const Modal = dynamic(() => import('./Modal'), {
loading: () => <p>Loading...</p>,
});
Gestion des scripts tiers
Google Analytics, publicités, plugins sociaux — autant de tueurs de performance. Le FCP de nombreux sites en souffre.
Utiliser next/script :
Next.js fournit le composant Script pour contrôler le moment de chargement :
import Script from 'next/script';
export default function Layout({ children }) {
return (
<>
{children}
{/* Google Analytics : après interactivité */}
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="lazyOnload"
/>
<Script id="ga-init" strategy="lazyOnload">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
{/* Facebook Pixel : chargement différé */}
<Script
src="https://connect.facebook.net/en_US/fbevents.js"
strategy="lazyOnload"
/>
</>
);
}
Valeurs de strategy :
beforeInteractive: avant interactivité (scripts critiques)afterInteractive: après interactivité (défaut)lazyOnload: en idle (recommandé pour analytics et pubs)worker: dans un Web Worker (expérimental)
En passant tous les scripts non essentiels en lazyOnload, le FCP a gagné 0,8 s.
Code splitting et lazy loading
Next.js fait du code splitting automatiquement, mais un affinage manuel reste utile.
Composants en lazy load :
Pour ce qui n’est pas visible au first paint :
import dynamic from 'next/dynamic';
// Lazy load du composant commentaires
const Comments = dynamic(() => import('./Comments'), {
loading: () => <div>Chargement des commentaires...</div>,
ssr: false, // Pas de rendu serveur
});
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
{/* Chargé quand l'utilisateur scroll jusqu'ici */}
<Comments postId={post.id} />
</article>
);
}
Lazy load des bibliothèques tierces :
Certaines librairies sont lourdes (graphiques, éditeurs riches) — chargez-les à la demande.
Cas : Chart.js — 800 Ko économisés
Un projet importait Chart.js sur chaque page alors que seul le dashboard l’utilisait. 800 Ko de JS inutile partout.
Avant optimisation :
// ❌ Erreur : import global, toutes les pages chargent
import { Chart } from 'chart.js';
export default function Dashboard() {
return <canvas ref={chartRef} />;
}
Après optimisation :
// ✅ Correct : chargement à la demande
import dynamic from 'next/dynamic';
const ChartComponent = dynamic(() => import('./ChartComponent'), {
loading: () => <div>Chargement du graphique...</div>,
ssr: false,
});
export default function Dashboard() {
return <ChartComponent />;
}
// ChartComponent.jsx
import { Chart } from 'chart.js';
export default function ChartComponent() {
return <canvas ref={chartRef} />;
}
Seuls les visiteurs du dashboard téléchargent Chart.js.
Lazy load des images :
Hors image LCP, les autres images doivent être en lazy load :
<Image
src="/feature-image.jpg"
width={600}
height={400}
loading="lazy" // Valeur par défaut, peut être omis
alt="Présentation des fonctionnalités"
/>
Surveillance et optimisation continue
L’optimisation n’est pas un one-shot. La performance se maintient dans la durée.
Lighthouse CI en automatique
Lancer Lighthouse à la main prend du temps et on oublie. Mieux vaut l’intégrer au CI/CD.
Installation :
npm install -D @lhci/cli
Configuration lighthouserc.js :
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/products'],
numberOfRuns: 3, // 3 exécutions, moyenne
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }], // Performance ≥ 90
'first-contentful-paint': ['error', { maxNumericValue: 2000 }], // FCP ≤ 2s
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }], // LCP ≤ 2,5s
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }], // CLS < 0,1
},
},
upload: {
target: 'temporary-public-storage',
},
},
};
GitHub Actions :
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm install
- run: npm run build
- run: npm run start &
- run: npx @lhci/cli autorun
Chaque push déclenche Lighthouse. Une régression de performance fait échouer le CI.
Real User Monitoring (RUM)
Lighthouse mesure en lab — l’expérience réelle peut différer. Collectez les Core Web Vitals terrain.
Vercel Analytics :
Sur Vercel, activation en un clic :
// app/layout.jsx
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
);
}
Bibliothèque web-vitals :
Sans Vercel, utilisez la lib Google web-vitals :
npm install web-vitals
// app/layout.jsx
'use client';
import { useEffect } from 'react';
import { onLCP, onFID, onCLS, onINP } from 'web-vitals';
function sendToAnalytics(metric) {
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify(metric),
});
}
export default function Analytics() {
useEffect(() => {
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
}, []);
return null;
}
Google Search Console :
Le rapport « Signaux Web essentiels » affiche les performances terrain et signale les pages à améliorer.
Pièges courants et solutions
Enfin, les 5 écueils les plus fréquents :
Piège 1 : trop de JavaScript côté client
Beaucoup de devs font tout en React — la page dépend entièrement du JS. Si le chargement échoue ou traîne, écran blanc.
Solutions :
- Privilégier les Server Components (défaut Next.js 13+)
'use client'uniquement si nécessaire- Progressive Enhancement : fonctionnalité de base d’abord, enrichissement ensuite
Piège 2 : ignorer la performance mobile
Rapide sur desktop ne veut pas dire rapide sur mobile. CPU faible, réseau lent — les problèmes s’amplifient.
Solutions :
- Tester en mode « Mobile » dans Lighthouse
- Throttling réseau (Chrome DevTools → Network)
- Throttling CPU (Chrome DevTools → Performance → CPU)
Piège 3 : scripts tiers non optimisés
Google Analytics, Facebook Pixel, Intercom, Hotjar… chaque script alourdit la page.
Solutions :
- Encapsuler avec
next/script,strategy="lazyOnload" - Audit régulier : quels scripts sont vraiment nécessaires ?
- Tracking serveur plutôt que client quand c’est possible
Piège 4 : mauvais choix de format d’image
Toujours en PNG et JPG ? WebP et AVIF économisent 30 à 50 % de volume.
Solutions :
Le composant Image Next.js convertit automatiquement — assurez-vous que le serveur le supporte :
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'], // Formats modernes en priorité
},
};
Piège 5 : négliger la performance serveur
Un frontend parfait ne compense pas un serveur lent.
Solutions :
- Optimiser les requêtes BDD (index, éviter N+1)
- Cache (Redis, CDN)
- Monitoring serveur (APM : New Relic, Datadog)
Résumé
Récapitulons :
Points clés LCP :
priorityetfetchPriority="high"sur l’image LCPnext/fontpour les polices- SSG et ISR en priorité, moins de calcul serveur
- CDN pour réduire le TTFB
Points clés CLS :
- Dimensions explicites pour images et médias
- Espace réservé pour contenu dynamique (pubs, bandeaux)
- Pas de layout responsive via hooks JavaScript
next/fontpour limiter les sauts de police
Points clés FCP :
- Différer les ressources non critiques
- Scripts tiers via
next/scriptaveclazyOnload - Lazy load des grosses librairies et composants hors first screen
Optimisation continue :
- Lighthouse CI automatisé
- Données utilisateurs réelles (RUM)
- Audit et nettoyage réguliers
L’optimisation performance suit un cycle « mesurer → optimiser → remesurer ». Pas de fix unique — attention et amélioration continues.
Quand le score Lighthouse a enfin dépassé 90, la satisfaction était réelle. Et la hausse de conversion a prouvé que l’effort en valait la peine.
Plan d’action
À vous de jouer. Je recommande cet ordre :
-
Immédiat :
- Tester votre projet Next.js avec Lighthouse
- Identifier l’élément LCP —
priorityest-il défini ? - Chercher du code
useMediaQueryou similaire qui provoque du CLS
-
Cette semaine :
- Optimiser l’image LCP (priority + fetchPriority)
- Migrer les polices vers
next/font - Ajouter des placeholders pour le contenu dynamique
-
Semaine prochaine :
- Scripts tiers en
lazyOnload - Lazy load des grosses librairies et composants hors first screen
- Configurer Lighthouse CI
- Scripts tiers en
-
En continu :
- Vérifier le score Lighthouse chaque mois
- Suivre le rapport Core Web Vitals dans Search Console
- Corriger rapidement les régressions
L’optimisation performance n’est pas un projet ponctuel — c’est un processus continu. Bonne optimisation !
Processus complet d'optimisation Core Web Vitals Next.js
Optimiser LCP, INP et CLS pour atteindre 90+ au Lighthouse
⏱️ Estimated time: 8 hr
- 1
Step 1: Optimiser le LCP (Largest Contentful Paint)
Objectif : ≤ 2,5 s
Méthodes :
• Utiliser next/image (conversion WebP/AVIF automatique)
• Ajouter priority aux images clés above-the-fold
• Précharger les ressources clés (<link rel="preload">)
• Optimiser le temps de réponse serveur (CDN, Edge Functions)
• Réduire les ressources bloquant le rendu (CSS/JS non critiques en différé)
• React Server Components pour moins de JS côté client
Outils :
• Rapport Performance Lighthouse
• Panneau Performance Chrome DevTools
• WebPageTest - 2
Step 2: Optimiser l'INP (Interaction to Next Paint)
Objectif : ≤ 200 ms
Méthodes :
• Réduire l'exécution JavaScript (code splitting, lazy loading)
• React Server Components pour moins de JS côté client
• Optimiser la gestion des événements (debounce, throttle, délégation)
• Éviter les longues tâches sur le thread principal (Web Workers)
• Optimiser les scripts tiers (chargement différé, async/defer)
• Suspense et rendu en streaming
Outils :
• Panneau Performance Chrome DevTools
• Extension Chrome Web Vitals
• Outils Real User Monitoring (RUM) - 3
Step 3: Optimiser le CLS (Cumulative Layout Shift)
Objectif : ≤ 0,1
Méthodes :
• Définir largeur/hauteur images et vidéos (ou aspect-ratio)
• Éviter l'insertion dynamique (réserver l'espace)
• font-display: swap pour limiter les décalages liés aux polices
• Espace réservé pour les publicités
• CSS Grid/Flexbox plutôt que positionnement absolu
• Éviter d'insérer du contenu au-dessus du contenu existant
Outils :
• Rapport CLS Lighthouse
• Enregistrements Layout Shift dans Chrome DevTools
• Visualisation CLS WebPageTest - 4
Step 4: Exploiter les fonctionnalités de performance Next.js
Astuces Next.js :
• Composant Image pour l'optimisation automatique
• Font Optimization pour les polices
• React Server Components pour moins de JS client
• Streaming SSR pour accélérer le first paint
• Dynamic Imports pour le code splitting
• next/dynamic pour le chargement différé
Vérifications :
• Configuration performance dans next.config.js
• Taille du bundle (npm run build) - 5
Step 5: Surveillance et optimisation continue
Outils de monitoring :
• Rapport Core Web Vitals Google Search Console
• Vercel Analytics (si déployé sur Vercel)
• Extension Chrome Web Vitals
• Outils Real User Monitoring (RUM)
Optimisation continue :
• Vérifier le score Lighthouse chaque mois
• Suivre le rapport Core Web Vitals Search Console
• Corriger rapidement les régressions
• A/B tests pour mesurer l'impact
FAQ
Quelles sont les trois métriques Core Web Vitals ?
Comment optimiser le LCP ?
Quelle différence entre INP et FID ?
Comment éviter le CLS (décalage de mise en page) ?
Combien de temps avant de voir les effets de l'optimisation ?
Quelles fonctionnalités Next.js aident à la performance ?
Comment surveiller les Core Web Vitals ?
15 min de lecture · Publié le: 19 déc. 2025 · Mis à jour le: 27 juil. 2026
Guide complet Next.js
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Optimisation d'images Next.js : guide complet du composant Image
Guide complet du composant Image Next.js : résoudre le chargement lent, les erreurs remotePatterns et le décalage de mise en page. Next.js 14/15, exemples de code et astuces pour gagner 60 à 80 % en performance.
Partie 26 sur 51
Suivant
Guide SEO Next.js complet : Metadata API + données structurées en pratique
Guide complet pour configurer le SEO avec la Metadata API de Next.js 15 : données structurées, Open Graph, Twitter Cards, exemples de code et pièges à éviter pour multiplier le trafic.
Partie 28 sur 51



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire