Changer le thème

Optimisation d'images Next.js : guide complet du composant Image

Easton editorial illustration: performance inspection lens

Score Lighthouse : 62.

Deux semaines sur un projet Next.js, et la note performance peine à dépasser la moyenne. J’ouvre Performance : le problème saute aux yeux — LCP (Largest Contentful Paint) à 4,8 s, entièrement tiré vers le bas par les images.

J’étais perplexe. Next.js, ce n’est pas censé optimiser tout seul ? En regardant le code : le grand Hero de la page d’accueil utilisait encore un <img> brut. Sur la liste e-commerce, des dizaines de vignettes en pleine résolution, 3 à 4 Mo chacune.

J’ai compris ensuite : Next.js est puissant, mais l’optimisation d’images exige d’utiliser activement le composant Image. Bien configuré, le volume peut baisser de 60 à 80 %, et le LCP passer de 4 s à moins de 2 s.

Beaucoup, comme moi au début, trébuchent malgré Image : erreur « Un-configured Host » sur les images distantes, zone qui saute au chargement, dizaines d’options incompréhensibles. Cet article rassemble les pièges que j’ai rencontrés et leurs solutions — du début à une utilisation correcte du composant Image Next.js.

Pourquoi utiliser le composant Image Next.js ?

Les trois pièges de la balise img classique

On pourrait penser qu’une image, c’est juste <img src="xxx" alt="Description du contenu visible">. Je pensais pareil — jusqu’aux tests de performance.

Piège 1 : format non optimisé, bande passante gaspillée

Une balise img affiche le format fourni. PNG de 3 Mo → l’utilisateur télécharge 3 Mo. Les navigateurs modernes supportent WebP (−30 % vs JPEG) et AVIF (−40 % de plus). img ne choisit rien : il charge le fichier tel quel.

Piège 2 : même image quelle que soit la taille d’écran

Surtout visible sur mobile : image 2000×1500, écran 375 px de large — le navigateur télécharge tout puis réduit. Bande passante et temps perdus.

Piège 3 : décalage de mise en page (CLS)

Vous voulez cliquer un bouton, l’image apparaît, la page descend, vous cliquez ailleurs. C’est le CLS (Cumulative Layout Shift), métrique Core Web Vitals de Google, directement liée au SEO.

Les capacités d’optimisation automatique d’Image

Le composant Image de Next.js n’est pas un simple wrapper img : c’est une solution complète.

Sélection automatique du format

Image lit l’en-tête Accept du navigateur : AVIF si supporté, sinon WebP, sinon format d’origine. Zéro code supplémentaire.

En test : JPEG 500 Ko → WebP 180 Ko, AVIF 120 Ko. Sur des dizaines ou centaines d’images, l’économie est énorme.

Chargement responsive

Image génère et charge la taille adaptée à l’écran : 375 px sur mobile, 1920 px sur desktop (srcset). Manuellement sur img, c’est lourd ; avec Image, c’est automatique.

Lazy loading

Par défaut, seules les images visibles dans le viewport se chargent. Le reste attend le scroll — moins de données au premier chargement, page plus rapide.

Bien utilisé, le composant Image Next.js réduit le volume de 60 à 80 %, maintient le LCP sous 2,5 s et le CLS proche de zéro. Ce ne sont pas des chiffres théoriques : je les ai mesurés sur un vrai projet.

Bases : images locales vs distantes

Au début, la question revient souvent : pourquoi certaines images passent et d’autres plantent ? Surtout une différence : local vs distant.

Images locales : le cas le plus simple

Deux approches courantes.

Méthode 1 : import (recommandée)

import heroImage from '/public/images/hero.jpg'
import Image from 'next/image'

export default function Home() {
  return (
    <Image
      src={heroImage}
      alt="Hero image"
    />
  )
}

Next.js lit largeur et hauteur au build : pas besoin de width et height. C’est ma méthode par défaut pour le local.

Méthode 2 : chemin direct

<Image
  src="/images/hero.jpg"
  width={1920}
  height={1080}
  alt="Hero image"
/>

Pour les fichiers dans public, chemin direct possible — mais width et height obligatoires, sinon erreur.

Images distantes : là où ça coince le plus

Images depuis une URL externe (stockage cloud, CDN tiers).

Erreur fréquente : « Un-configured Host »

<Image
  src="https://images.unsplash.com/photo-123456"
  width={800}
  height={600}
  alt="Sample image"
/>

Sans configuration, vous verrez souvent :

Error: Invalid src prop (https://images.unsplash.com/photo-123456) on `next/image`, 
hostname "images.unsplash.com" is not configured under images in your `next.config.js`

Next.js limite les domaines autorisés pour éviter qu’un attaquant abuse de votre API d’optimisation.

Solution : remotePatterns

Dans next.config.js (recommandé Next.js 14+) :

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'images.unsplash.com',
        port: '',
        pathname: '/**',
      },
      {
        protocol: 'https',
        hostname: 's3.amazonaws.com',
        port: '',
        pathname: '/my-bucket/**',
      },
    ],
  },
}

module.exports = nextConfig

Signification des champs :

  • protocol : https ou http, en général https
  • hostname : domaine, correspondance exacte
  • port : en général vide
  • pathname : /** pour tout, ou un préfixe précis

Redémarrez le serveur de dev après modification — j’ai perdu une demi-heure une fois en oubliant ça.

Ancienne config (déconseillée)

module.exports = {
  images: {
    domains: ['images.unsplash.com', 's3.amazonaws.com'],
  },
}

domains est déprécié depuis Next.js 14. Préférez remotePatterns, plus sûr (chemins restreignables).

Pourquoi width et height sont obligatoires

Sauf import local, il faut width et height pour éviter le CLS.

Le navigateur doit réserver l’espace avant le téléchargement. Sans dimensions, il attend la fin du chargement et la mise en page bouge.

Pour le responsive à largeur variable, voir fill plus bas.

Résoudre le décalage de mise en page (optimisation CLS)

Une page qui saute, c’est insupportable. Sur un site d’actu, la plainte numéro un : « je visais le titre, l’image a chargé, j’ai cliqué sur une pub ». Le CLS compte autant pour le SEO que pour l’UX.

Qu’est-ce que le CLS et pourquoi c’est important

CLS (Cumulative Layout Shift) mesure le déplacement des éléments pendant le chargement.

Google en fait une métrique Core Web Vitals. Au-delà de 0,1, c’est mauvais ; en dessous, c’est bon. Une page avec vingt images qui poussent le contenu à chaque chargement accumule vite un mauvais score.

Personnellement, je ferme les pages qui tremblent sans même lire le contenu.

Comment Image évite le CLS

Principe : réserver l’espace à l’avance.

Avec width et height, le navigateur trace un cadre avant le téléchargement ; l’image remplit le cadre sans déplacer le reste.

<Image
  src="/product.jpg"
  width={400}
  height={300}
  alt="Product image"
/>

CLS proche de zéro. Mais le responsive pur avec largeur fluide demande autre chose.

Images responsives : la propriété fill

fill fait remplir l’image le conteneur parent ; les dimensions viennent du CSS.

<div style={{ position: 'relative', width: '100%', height: '400px' }}>
  <Image
    src="/hero.jpg"
    fill
    style={{ objectFit: 'cover' }}
    alt="Hero image"
  />
</div>

Points clés :

  1. Parent avec position: relative
  2. Hauteur explicite sur le parent (pas height: auto seul)
  3. objectFit : cover remplit en rognant, contain affiche tout (bandes possibles)

Le navigateur voit la hauteur du parent et réserve l’espace — CLS maîtrisé.

sizes : indiquer quelle taille charger

Avec fill, ajoutez sizes, sinon Next.js ne sait pas quelle variante générer.

<div style={{ position: 'relative', width: '100%', height: '400px' }}>
  <Image
    src="/hero.jpg"
    fill
    sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
    style={{ objectFit: 'cover' }}
    alt="Hero image"
  />
</div>

Signification de sizes :

  • ≤ 768 px : 100 % de la largeur du viewport
  • 768–1200 px : 50 %
  • > 1200 px : 33 %

Next.js génère plusieurs tailles ; le navigateur choisit la bonne. Moins de données sur mobile, chargement plus rapide.

Cas pratiques

Hero pleine largeur (above-the-fold)

<div style={{ position: 'relative', width: '100%', height: '60vh' }}>
  <Image
    src="/hero.jpg"
    fill
    priority
    sizes="100vw"
    style={{ objectFit: 'cover' }}
    alt="Hero image"
  />
</div>

priority pour charger en priorité ; sizes="100vw" = pleine largeur viewport.

Vignette d’article (taille fixe)

<Image
  src={post.thumbnail}
  width={300}
  height={200}
  alt={post.title}
/>

Dimensions fixes : width et height suffisent.

Grille produits (responsive)

<div style={{ position: 'relative', width: '100%', paddingBottom: '100%' }}>
  <Image
    src={product.image}
    fill
    sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
    style={{ objectFit: 'cover' }}
    alt={product.name}
  />
</div>

paddingBottom: '100%' crée un carré 1:1 — une colonne sur mobile, deux sur tablette, trois sur desktop.

Configuration clé pour la performance

Après les bases et le CLS, quelques réglages pour gagner encore.

priority : images critiques above-the-fold

Par défaut, lazy loading : chargement à l’entrée dans le viewport. Pour le Hero ou le logo, vous voulez l’inverse.

<Image
  src="/hero.jpg"
  width={1920}
  height={1080}
  priority
  alt="Hero image"
/>

Avec priority, Next.js :

  1. Désactive le lazy loading
  2. Insère un preload dans le <head>
  3. Améliore nettement le LCP

Sur un projet, Hero + priority : LCP 3,8 s → 2,1 s.

Quand utiliser priority ?

  1. Hero de la page d’accueil
  2. Logo (si volumineux)
  3. Image principale d’un article
  4. Toute image candidate au LCP

Ne mettez pas priority partout : vingt images en parallèle ralentissent tout. Réservez-le aux plus importantes.

Changement Next.js 16

En RC, priority devient preload :

<Image
  src="/hero.jpg"
  width={1920}
  height={1080}
  preload
  alt="Hero image"
/>

Ou loading="eager" + fetchPriority="high" :

<Image
  src="/hero.jpg"
  width={1920}
  height={1080}
  loading="eager"
  fetchPriority="high"
  alt="Hero image"
/>

loading : stratégie de chargement

  • lazy (défaut) : chargement à l’approche du viewport
  • eager : chargement immédiat

En général, lazy suffit ; eager pour le above-the-fold.

// Bas de page : lazy par défaut
<Image src="/related-1.jpg" width={300} height={200} alt="Related post" />

// Contenu principal above-the-fold
<Image src="/main-content.jpg" width={800} height={600} loading="eager" alt="Main content" />

quality : équilibre qualité / taille

quality de 1 à 100, défaut 75.

<Image
  src="/product.jpg"
  width={800}
  height={600}
  quality={90}
  alt="Product image"
/>

Repères :

  • Images clés above-the-fold : quality={90}
  • Contenu courant : quality={75} (défaut)
  • Vignettes, arrière-plans : quality={60}

En test : 90 → 75, différence visuelle faible, −30 % de volume ; 75 → 60, encore acceptable, −20 % de plus.

Mise à jour Next.js 16 : quality devient obligatoire (limiter les abus via paramètres URL). Après migration, ajoutez-le sur chaque Image.

Formats automatiques : WebP vs AVIF

Automatique via l’en-tête Accept :

  • AVIF si supporté (plus petit, encodage plus lent)
  • Sinon WebP (bon compromis)
  • Sinon JPEG/PNG d’origine

AVIF ≈ −30 à 40 % vs WebP ; Chrome 85+, Firefox 93+, Safari 16+ le supportent.

Exemples combinés

Hero (priorité, haute qualité)

<div style={{ position: 'relative', width: '100%', height: '60vh' }}>
  <Image
    src="/hero.jpg"
    fill
    priority
    quality={90}
    sizes="100vw"
    style={{ objectFit: 'cover' }}
    alt="Welcome to our site"
  />
</div>

Vignettes liste (lazy, qualité moyenne)

{posts.map(post => (
  <Image
    key={post.id}
    src={post.thumbnail}
    width={300}
    height={200}
    quality={75}
    alt={post.title}
  />
))}

Icônes footer (lazy, basse qualité)

<Image
  src="/footer-icon.png"
  width={40}
  height={40}
  quality={60}
  alt="Footer icon"
/>

Erreurs courantes et solutions

Erreur 1 : Un-configured Host

Message :

Error: Invalid src prop (https://example.com/image.jpg) on `next/image`, 
hostname "example.com" is not configured under images in your `next.config.js`

Cause : URL externe sans entrée dans remotePatterns.

Solution :

module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
        port: '',
        pathname: '/**',
      },
    ],
  },
}

Notes :

  1. Redémarrer le serveur (npm run dev)
  2. hostname exact, pas de wildcard *.example.com
  3. Plusieurs domaines = plusieurs objets dans le tableau

Erreur 2 : décalage / CLS élevé

Symptôme : la page saute à l’arrivée des images.

Causes :

  1. Pas de width/height
  2. fill sans hauteur sur le parent

Solutions :

// ❌ Manque width/height
<Image src="/product.jpg" alt="Product" />

// ✅ Dimensions explicites
<Image src="/product.jpg" width={400} height={300} alt="Product" />
// ❌ Parent sans hauteur
<div style={{ position: 'relative', width: '100%' }}>
  <Image src="/hero.jpg" fill alt="Hero" />
</div>

// ✅ Parent avec hauteur
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
  <Image src="/hero.jpg" fill alt="Hero" />
</div>

Erreur 3 : flou ou image trop lourde sur mobile

Cause : pas de sizes → défaut 100vw même si l’image n’occupe que la moitié de l’écran.

Solution :

<Image
  src="/product.jpg"
  width={400}
  height={300}
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
  alt="Product"
/>

Taille fixe (vignette) : width/height sans sizes.

Erreur 4 : API dépréciées

domains (Next.js 14+)

// ❌ Déprécié
module.exports = {
  images: {
    domains: ['example.com'],
  },
}

// ✅ remotePatterns
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
      },
    ],
  },
}

onLoadingComplete

// ❌ Déprécié
<Image
  src="/image.jpg"
  width={400}
  height={300}
  onLoadingComplete={() => console.log('loaded')}
  alt="Image"
/>

// ✅ onLoad
<Image
  src="/image.jpg"
  width={400}
  height={300}
  onLoad={() => console.log('loaded')}
  alt="Image"
/>

Next.js 16 : prioritypreload ou loading="eager" + fetchPriority="high".

Checklist de dépannage

  1. remotePatterns pour les images distantes ?
  2. ✅ Serveur redémarré après config ?
  3. width/height ou parent dimensionné ?
  4. ✅ Avec fill : position: relative + hauteur sur le parent ?
  5. sizes cohérent avec le layout ?
  6. ✅ Pas de domains ou onLoadingComplete ?

Astuces avancées et bonnes pratiques

placeholder pour une meilleure UX

blur (images locales importées)

import Image from 'next/image'
import heroImage from '/public/hero.jpg'

export default function Hero() {
  return (
    <Image
      src={heroImage}
      placeholder="blur"
      alt="Hero image"
    />
  )
}

Next.js génère un base64 flou ; effet type Instagram/Medium.

Images distantes

Fournir blurDataURL manuellement :

<Image
  src="https://example.com/image.jpg"
  width={800}
  height={600}
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
  alt="Remote image"
/>

Outil en ligne blurred.dev ou génération serveur avec sharp.

empty : pas de placeholder (comportement par défaut).

Utilisation avec un CDN

Loader Cloudinary :

// next.config.js
module.exports = {
  images: {
    loader: 'cloudinary',
    path: 'https://res.cloudinary.com/your-cloud-name/',
  },
}

Loader personnalisé :

// next.config.js
module.exports = {
  images: {
    loader: 'custom',
    loaderFile: './my-loader.js',
  },
}

// my-loader.js
export default function myLoader({ src, width, quality }) {
  return `https://cdn.example.com/${src}?w=${width}&q=${quality || 75}`
}

Utile pour le trafic élevé.

Schéma responsive complet

<div className="image-container">
  <Image
    src="/product.jpg"
    width={1200}
    height={800}
    sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
    style={{
      width: '100%',
      height: 'auto',
    }}
    alt="Product image"
  />
</div>
.image-container {
  width: 100%;
}

@media (min-width: 640px) {
  .image-container {
    width: 50%;
  }
}

@media (min-width: 1024px) {
  .image-container {
    width: 33.333%;
  }
}

Alignez sizes et les media queries CSS.

Surveillance et debug

Chrome DevTools → Network → Img : taille, temps, en-têtes (format). Les URLs optimisées portent souvent ?w=xxx&q=xxx.

Lighthouse : LCP < 2,5 s, CLS < 0,1, suggestions sur les images non optimisées. Après optimisation images, je passe souvent de 60+ à 90+.

Production : Google Search Console ou Vercel Analytics pour suivre les Core Web Vitals.

Conclusion

Le composant Image Next.js résout trois problèmes : chargement lent, erreurs de config, décalage de mise en page.

Bien utilisé :

  • −60 à 80 % de volume (WebP/AVIF)
  • LCP sous 2,5 s (priority ciblé)
  • CLS proche de zéro (dimensions ou fill)

À retenir :

  1. remotePatterns + redémarrage serveur
  2. width/height ou fill + hauteur parent
  3. priority sur le above-the-fold seulement
  4. sizes pour le responsive
  5. quality : 90 / 75 / 60 selon l’importance

Remplacez vos &lt;img&gt; par <Image>, lancez Lighthouse — je parie sur +20 points minimum.

En cas de blocage, repassez par la section erreurs courantes — la réponse s’y trouve en général. Beaucoup d’options, mais quelques réglages centraux couvrent la majorité des cas.

Processus complet d'optimisation du composant Image Next.js

De la configuration des images distantes à l'optimisation performance et la prévention du CLS

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: Configurer les domaines d'images distantes

    Dans next.config.js :
    • Ajouter le tableau images.remotePatterns
    • Définir protocol, hostname, pathname
    • Correspondance par motif de chemin

    Exemple :
    images: {
    remotePatterns: [
    {
    protocol: 'https',
    hostname: 'example.com',
    pathname: '/images/**'
    }
    ]
    }

    Note : redémarrer le serveur de dev après modification
  2. 2

    Step 2: Remplacer img par le composant Image

    Usage de base :
    • import Image from 'next/image'
    • width et height obligatoires (ou fill)
    • alt pour le SEO

    Exemple :
    <Image
    src="/hero.jpg"
    width={800}
    height={600}
    alt="Texte descriptif"
    />
  3. 3

    Step 3: Gérer le décalage de mise en page (CLS)

    Méthode 1 : dimensions fixes
    • width et height requis
    • aspect-ratio pour conserver les proportions

    Méthode 2 : mode fill
    • parent en position: relative
    • fill sur Image
    • hauteur explicite sur le parent

    Méthode 3 : placeholder
    • blurDataURL pour le flou
    • placeholder="blur"
  4. 4

    Step 4: Optimiser le chargement

    Images clés above-the-fold :
    • attribut priority
    • quality=90
    • visibles dans le viewport

    Autres images :
    • lazy loading par défaut
    • quality=75
    • sizes pour le responsive

    Vignettes :
    • quality=60
    • petites dimensions
  5. 5

    Step 5: Configurer les images responsives

    Propriété sizes :
    • indique la taille nécessaire par breakpoint
    • le navigateur choisit la variante optimale

    Exemple :
    <Image
    src="/hero.jpg"
    width={1200}
    height={630}
    sizes="(max-width: 768px) 100vw, 50vw"
    alt="Description"
    />

    Mobile : pleine largeur ; desktop : 50 % du viewport
  6. 6

    Step 6: Tester et valider

    Tests performance :
    • Lighthouse pour LCP et CLS
    • Network pour le chargement des images
    • Vérifier WebP/AVIF

    Checklist :
    • Tous les domaines distants configurés
    • width/height sur chaque image
    • priority sur le above-the-fold
    • CLS proche de 0
    • Volume réduit d'au moins 60 %

FAQ

Pourquoi l'erreur « Un-configured Host » sur une image distante ?
Par sécurité, Next.js exige de déclarer les domaines autorisés dans images.remotePatterns (protocol, hostname, pathname optionnel). Redémarrez le serveur de dev après modification.
Faut-il obligatoirement width et height sur Image ?
Oui, pour éviter le CLS. Sinon utilisez fill avec un parent dimensionné, ou aspect-ratio. Sans dimensions, le score CLS se dégrade.
Comment éviter le décalage au chargement des images ?
1) width et height ; 2) fill + conteneur parent ; 3) placeholder="blur" ; 4) aspect-ratio. L'essentiel : un conteneur à taille connue avant le chargement.
Quand utiliser priority ?
Pour les images critiques above-the-fold (Hero, logo, image principale d'article). Désactive le lazy loading et améliore le LCP. À utiliser avec parcimonie.
Image convertit-il automatiquement le format ?
Oui : WebP si le navigateur le supporte, AVIF en priorité si disponible. Réduction typique de 60 à 80 % du volume.
À quoi sert sizes ?
Indique au navigateur quelle largeur d'image charger selon le viewport. Essentiel pour le responsive et éviter de télécharger une image trop grande sur mobile.
Comment optimiser la qualité des images ?
quality : 90 pour le above-the-fold, 75 par défaut, 60 pour les vignettes. Équilibrez clarté et poids ; combinez avec des dimensions adaptées par breakpoint.

11 min de lecture · Publié le: 19 déc. 2025 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog