Cours Next.js · Module 3: Styles et optimisation visuelle
Stratégies de chargement des images (lazy, eager, priority) dans Next.js 16
Dans cette leçon13
À Metropolis Quantum, le système de transport de la ville est organisé selon différentes priorités. Les véhicules d'urgence ont la priorité la plus élevée et ont toujours la voie libre. Les transports en commun ont une priorité moyenne, et les véhicules privés la plus basse. Cette gestion intelligente du trafic garantit que les ressources les plus importantes atteignent toujours leur destination le plus vite, tandis que les moins critiques peuvent attendre.
De même, dans les applications web, les différentes images ont différents niveaux d'importance. L'image hero de la page devrait se charger immédiatement, tandis que les images placées plus bas peuvent n'être chargées que lorsque l'utilisateur les fait défiler. Next.js 16 offre différentes stratégies de chargement des images qui te permettent de personnaliser leur chargement en fonction de leur importance et de leur position sur la page.
Trois grandes stratégies de chargement des images
Dans Next.js 16, nous disposons de trois grandes stratégies de chargement des images :
- Lazy - la stratégie par défaut, les images ne sont chargées que lorsqu'elles approchent de la zone visible (viewport)
- Eager - les images sont chargées immédiatement, quelle que soit leur position sur la page
- Priority - les images sont chargées avec la priorité la plus élevée, idéal pour les images « above the fold ». Dans Next.js 16, tu actives cette stratégie avec l'attribut
preload, qui a remplacé l'attributpriority, désormais déprécié
Chacune de ces stratégies a son cas d'usage et son impact sur la performance de la page.
Lazy loading
Le lazy loading est la stratégie par défaut du composant Image dans Next.js 16. Il fonctionne selon un principe simple :
Ne charge que ce que l'utilisateur voit actuellement ou verra bientôt.
Comment fonctionne le lazy loading ?
- Next.js retarde automatiquement le chargement des images situées hors de la zone visible (viewport)
- Les images ne sont chargées que lorsque l'utilisateur fait défiler la page et qu'elles approchent de la zone visible
- En coulisses, on utilise la fonctionnalité moderne du navigateur
loading="lazy", avec des optimisations supplémentaires
Exemple d'utilisation du lazy loading :
1import Image from 'next/image';
2
3export default function QuantumGallery() {
4 return (
5 <div className="gallery">
6 {Array.from({ length: 20 }).map((_, index) => (
7 <div key={index} className="relative h-64 w-full">
8 <Image
9 src={`/images/quantum-city-${index + 1}.jpg`}
10 alt={`Metropolis Quantum - photo ${index + 1}`}
11 fill
12 style={{ objectFit: 'cover' }}
13 // No need to add loading="lazy" - it is the default value
14 />
15 </div>
16 ))}
17 </div>
18 );
19}Avantages du lazy loading :
- Un chargement initial de page plus rapide - seules les images critiques sont chargées immédiatement
- Une moindre consommation de données - les images que l'utilisateur n'atteint jamais ne sont pas chargées
- Une meilleure performance - le navigateur peut concentrer ses ressources sur les éléments actuellement importants
Scénarios où le lazy loading vaut la peine :
- Les pages longues avec beaucoup d'images
- Les galeries de photos
- Les listes de produits
- Les carrousels d'images
- Les pages à défilement infini
Eager loading
La stratégie eager loading est l'opposée du lazy loading. Au lieu d'attendre que les images approchent de la zone visible, elles sont chargées immédiatement pendant le chargement de la page.
Comment activer l'eager loading :
1import Image from 'next/image';
2
3export default function QuantumLogo() {
4 return (
5 <Image
6 src="/images/quantum-logo.png"
7 alt="Logo Metropolis Quantum"
8 width={200}
9 height={100}
10 loading="eager"
11 />
12 );
13}Avantages de l'eager loading :
- Une disponibilité immédiate - toutes les images sont disponibles dès le chargement de la page
- Aucun délai - l'utilisateur n'a pas à attendre le chargement des images pendant le défilement
- Un comportement plus prévisible - toutes les ressources sont chargées en un seul cycle
Scénarios où l'eager loading vaut la peine :
- Les pages courtes avec peu d'images
- Les pages où toutes les images sont importantes pour le contexte
- Les applications où un défilement fluide sans délai est crucial
- Les applications offline-first qui veulent charger toutes les ressources d'un coup
Priority loading
Le priority loading est une stratégie spéciale pour les images les plus importantes de la page, en particulier celles visibles « above the fold » (dans la partie supérieure de la page, visibles sans défilement).
Comment activer le priority loading :
1import Image from 'next/image';
2
3export default function QuantumHero() {
4 return (
5 <Image
6 src="/images/quantum-hero.jpg"
7 alt="Bienvenue à Metropolis Quantum"
8 width={1920}
9 height={1080}
10 preload
11 />
12 );
13}Quand tu utilises l'attribut preload (appelé priority dans Next.js 15 et les versions précédentes) :
- Next.js ajoute dans le
<head>une balise<link rel="preload">pour cette image - Le navigateur commence tout de suite à télécharger l'image, avant de l'atteindre dans le contenu de la page
- Le plus grand élément de la page (LCP) apparaît plus tôt
L'ancien attribut priority fonctionne encore dans Next.js 16, mais il est déprécié. La documentation conseille aussi de n'utiliser preload que lorsque l'image est à coup sûr l'élément LCP ; dans les autres cas, choisis loading="eager" ou fetchPriority="high".
Avantages du priority loading :
- Un LCP (Largest Contentful Paint) plus faible - une métrique clé des Core Web Vitals
- Un affichage plus rapide des images les plus importantes - une meilleure performance perçue
- L'optimisation des Core Web Vitals - un impact positif sur le SEO
Scénarios où priority vaut la peine :
- Les images hero de la page principale
- Le logo de la marque
- Les images de produit sur les pages de détail
- Les avatars des utilisateurs dans les applications sociales
- Toute image qui constitue le contenu principal de la page
Remarques clés sur l'attribut preload :
- Utilise
preloadavec parcimonie - uniquement pour les images les plus importantes - La documentation de Next.js recommande
preloaduniquement pour l'image LCP (Largest Contentful Paint), et non pour plusieurs images susceptibles de le devenir selon la largeur de l'écran - Trop d'images avec
preloadpeut paradoxalement réduire la performance
Comparaison des stratégies de chargement
| Stratégie | Attribut | Cas d'usage | Impact sur la performance |
|---|---|---|---|
| Lazy | loading="lazy" (par défaut) | Images sous la ligne de flottaison, galeries, listes | Chargement initial plus rapide, économie de données |
| Eager | loading="eager" | Petites pages, toutes les images importantes | Chargement initial plus lent, meilleure réactivité au défilement |
| Priority | preload (anciennement priority) | Images « above the fold », images hero, contenu principal | Meilleur LCP, affichage plus rapide des images clés |
Stratégies avancées de chargement des images
Au-delà des stratégies de base, Next.js 16 permet des approches plus avancées du chargement des images :
1. Précharger les images importantes avec useRouter
Parfois, nous voulons précharger les images d'une page vers laquelle l'utilisateur va probablement naviguer :
1'use client';
2
3import { useEffect } from 'react';
4import { useRouter } from 'next/navigation';
5import Image from 'next/image';
6
7export default function ProductCard({ product, isPriority }) {
8 const router = useRouter();
9
10 useEffect(() => {
11 // Preload product detail image on mouse hover
12 const preloadDetailImage = () => {
13 router.prefetch(`/products/${product.id}`);
14 };
15
16 const cardElement = document.getElementById(`product-card-${product.id}`);
17 if (cardElement) {
18 cardElement.addEventListener('mouseenter', preloadDetailImage);
19 return () => {
20 cardElement.removeEventListener('mouseenter', preloadDetailImage);
21 };
22 }
23 }, [product.id, router]);
24
25 return (
26 <div
27 id={`product-card-${product.id}`}
28 className="relative h-64 overflow-hidden rounded-lg"
29 onClick={() => router.push(`/products/${product.id}`)}
30 >
31 <Image
32 src={product.thumbnail}
33 alt={product.title}
34 fill
35 style={{ objectFit: 'cover' }}
36 preload={isPriority}
37 />
38 <div className="absolute bottom-0 w-full bg-black/60 p-4">
39 <h3 className="text-white font-bold">{product.title}</h3>
40 </div>
41 </div>
42 );
43}2. Bascule dynamique de la priorité selon la visibilité
Nous pouvons attribuer dynamiquement une priorité aux images actuellement visibles :
1'use client';
2
3import { useState, useEffect, useRef } from 'react';
4import Image from 'next/image';
5
6export default function DynamicPriorityImage({ src, alt }) {
7 const [isPriority, setIsPriority] = useState(false);
8 const imageRef = useRef(null);
9
10 useEffect(() => {
11 const observer = new IntersectionObserver(
12 ([entry]) => {
13 if (entry.isIntersecting) {
14 setIsPriority(true);
15 observer.disconnect();
16 }
17 },
18 { threshold: 0.1 }
19 );
20
21 if (imageRef.current) {
22 observer.observe(imageRef.current);
23 }
24
25 return () => {
26 observer.disconnect();
27 };
28 }, []);
29
30 return (
31 <div ref={imageRef} className="relative h-64 w-full">
32 <Image
33 src={src}
34 alt={alt}
35 fill
36 style={{ objectFit: 'cover' }}
37 preload={isPriority}
38 />
39 </div>
40 );
41}3. Chargement progressif des images
Tu peux combiner les stratégies pour d'abord afficher une image de basse qualité, puis la remplacer par une version de haute qualité :
1'use client';
2
3import { useState } from 'react';
4import Image from 'next/image';
5
6export default function ProgressiveImage() {
7 const [isLoaded, setIsLoaded] = useState(false);
8
9 return (
10 <div className="relative h-96 w-full">
11 {/* Niskokwalitywowy placeholder */}
12 <Image
13 src="/images/quantum-city-low.jpg"
14 alt="Metropolis Quantum"
15 fill
16 style={{
17 objectFit: 'cover',
18 opacity: isLoaded ? 0 : 1,
19 transition: 'opacity 0.5s ease-in-out'
20 }}
21 preload // Placeholder loaded immediately
22 />
23
24 {/* High quality image */}
25 <Image
26 src="/images/quantum-city-high.jpg"
27 alt="Metropolis Quantum"
28 fill
29 style={{
30 objectFit: 'cover',
31 opacity: isLoaded ? 1 : 0,
32 transition: 'opacity 0.5s ease-in-out'
33 }}
34 onLoad={() => setIsLoaded(true)}
35 />
36 </div>
37 );
38}Optimisation des images pour différents appareils
Les stratégies de chargement peuvent être combinées à des techniques d'adaptation des images aux différents appareils :
1. Images responsives avec différentes sources
1import Image from 'next/image';
2
3export default function ResponsiveHero() {
4 return (
5 <>
6 {/* Image for mobile devices */}
7 <div className="md:hidden">
8 <Image
9 src="/images/hero-mobile.jpg"
10 alt="Metropolis Quantum"
11 width={640}
12 height={960}
13 preload // Priority for the main image
14 />
15 </div>
16
17 {/* Image for tablets */}
18 <div className="hidden md:block lg:hidden">
19 <Image
20 src="/images/hero-tablet.jpg"
21 alt="Metropolis Quantum"
22 width={1024}
23 height={768}
24 preload
25 />
26 </div>
27
28 {/* Image for desktops */}
29 <div className="hidden lg:block">
30 <Image
31 src="/images/hero-desktop.jpg"
32 alt="Metropolis Quantum"
33 width={1920}
34 height={1080}
35 preload
36 />
37 </div>
38 </>
39 );
40}2. Chargement adaptatif selon la connexion
1'use client';
2
3import { useState, useEffect } from 'react';
4import Image from 'next/image';
5
6export default function AdaptiveLoadingImage() {
7 const [connectionType, setConnectionType] = useState('high');
8
9 useEffect(() => {
10 // Detect connection quality
11 const connection = navigator.connection ||
12 navigator.mozConnection ||
13 navigator.webkitConnection;
14
15 if (connection) {
16 if (connection.effectiveType === '4g' && !connection.saveData) {
17 setConnectionType('high');
18 } else {
19 setConnectionType('low');
20 }
21
22 // Listen for changes in connection quality
23 const updateConnectionStatus = () => {
24 if (connection.effectiveType === '4g' && !connection.saveData) {
25 setConnectionType('high');
26 } else {
27 setConnectionType('low');
28 }
29 };
30
31 connection.addEventListener('change', updateConnectionStatus);
32 return () => connection.removeEventListener('change', updateConnectionStatus);
33 }
34 }, []);
35
36 return (
37 <div className="relative h-64 w-full">
38 <Image
39 src={connectionType === 'high' ? '/images/high-quality.jpg' : '/images/low-quality.jpg'}
40 alt="Image adaptative"
41 fill
42 style={{ objectFit: 'cover' }}
43 loading={connectionType === 'high' ? 'eager' : 'lazy'}
44 quality={connectionType === 'high' ? 90 : 50}
45 />
46 </div>
47 );
48}N'oublie pas que dans Next.js 16, les deux valeurs de quality (90 et 50) doivent figurer dans la liste images.qualities de next.config. Par défaut, cette liste ne contient que 75 : sans l'étendre, les deux variantes recevront la qualité 75.
Implémenter des stratégies de chargement avancées dans une application réelle
Exemple : page produit avec différentes stratégies de chargement
1// app/products/[id]/page.tsx
2import Image from 'next/image';
3import { getProduct, getRelatedProducts } from '@/lib/api';
4import RelatedProducts from '@/components/RelatedProducts';
5
6export default async function ProductPage({ params }) {
7 const product = await getProduct((await params).id);
8 const relatedProducts = await getRelatedProducts((await params).id);
9
10 return (
11 <div className="container mx-auto px-4 py-8">
12 <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
13 {/* Main product image - priority loading */}
14 <div className="relative aspect-square">
15 <Image
16 src={product.mainImage}
17 alt={product.name}
18 fill
19 style={{ objectFit: 'contain' }}
20 preload
21 sizes="(max-width: 768px) 100vw, 50vw"
22 />
23 </div>
24
25 <div>
26 <h1 className="text-3xl font-bold mb-4">{product.name}</h1>
27 <p className="text-xl mb-4">{product.price}</p>
28 <div className="prose max-w-none">
29 <p>{product.description}</p>
30 </div>
31
32 <button className="mt-6 bg-blue-600 text-white px-6 py-3 rounded-md">
33 Ajouter au panier
34 </button>
35 </div>
36 </div>
37
38 {/* Additional photos gallery - lazy loading (default) */}
39 <div className="mt-12">
40 <h2 className="text-2xl font-bold mb-6">Galerie du produit</h2>
41 <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
42 {product.gallery.map((image, index) => (
43 <div key={index} className="relative aspect-square">
44 <Image
45 src={image.url}
46 alt={`${product.name} - photo ${index + 1}`}
47 fill
48 style={{ objectFit: 'cover' }}
49 sizes="(max-width: 768px) 50vw, 25vw"
50 // Default lazy loading
51 />
52 </div>
53 ))}
54 </div>
55 </div>
56
57 {/* Produits similaires - eager loading pour le premier élément */}
58 <div className="mt-12">
59 <h2 className="text-2xl font-bold mb-6">Podobne produkty</h2>
60 <RelatedProducts products={relatedProducts} />
61 </div>
62 </div>
63 );
64}1// components/RelatedProducts.tsx
2'use client';
3
4import Image from 'next/image';
5import Link from 'next/link';
6import { useRef, useEffect } from 'react';
7
8export default function RelatedProducts({ products }) {
9 const containerRef = useRef(null);
10
11 useEffect(() => {
12 // Preload images when hovering over the section
13 const preloadImages = () => {
14 products.forEach(product => {
15 const img = new Image();
16 img.src = product.image;
17 });
18 };
19
20 const container = containerRef.current;
21 if (container) {
22 container.addEventListener('mouseenter', preloadImages, { once: true });
23 return () => {
24 container.removeEventListener('mouseenter', preloadImages);
25 };
26 }
27 }, [products]);
28
29 return (
30 <div ref={containerRef} className="grid grid-cols-2 md:grid-cols-4 gap-4">
31 {products.map((product, index) => (
32 <Link key={product.id} href={`/products/${product.id}`}>
33 <div className="relative aspect-square rounded-lg overflow-hidden">
34 <Image
35 src={product.image}
36 alt={product.name}
37 fill
38 style={{ objectFit: 'cover' }}
39 // Eager loading uniquement pour le premier produit
40 loading={index === 0 ? 'eager' : 'lazy'}
41 sizes="(max-width: 768px) 50vw, 25vw"
42 />
43 <div className="absolute bottom-0 w-full bg-black/60 p-2">
44 <p className="text-white text-sm font-medium truncate">
45 {product.name}
46 </p>
47 </div>
48 </div>
49 </Link>
50 ))}
51 </div>
52 );
53}Impact des stratégies de chargement sur les Core Web Vitals
Les Core Web Vitals sont un ensemble de métriques Google qui mesurent l'expérience utilisateur sur les sites web. Le choix de la bonne stratégie de chargement des images a un impact direct sur ces métriques :
1. LCP (Largest Contentful Paint)
Le LCP mesure le temps de chargement du plus grand élément visible dans le viewport, qui est souvent une image.
Impact des stratégies de chargement :
- Le priority loading de l'image principale peut considérablement améliorer le LCP
- L'eager loading de toutes les images peut dégrader le LCP en dispersant les ressources
- Le lazy loading des images sous la ligne de flottaison n'affecte pas le LCP mais améliore la performance globale
2. CLS (Cumulative Layout Shift)
Le CLS mesure l'instabilité de la mise en page pendant le chargement.
Impact des stratégies de chargement :
- Toutes les stratégies peuvent minimiser le CLS si les images ont des dimensions spécifiées
- Le priority loading avec
placeholder="blur"offre le meilleur CLS car il réserve l'espace immédiatement - Spécifie toujours
widthetheightou utilisefillavec un conteneur aux dimensions définies
3. FID (First Input Delay)
Le FID mesure le temps entre la première interaction de l'utilisateur et le moment où le navigateur peut y répondre.
Impact des stratégies de chargement :
- Le lazy loading peut améliorer le FID en réduisant le travail du navigateur pendant le chargement
- L'eager loading peut dégrader le FID en augmentant la charge du thread principal
- Le priority loading des images clés peut être un compromis
Mesurer et optimiser les stratégies de chargement
Pour t'assurer que les stratégies de chargement choisies fonctionnent de manière optimale, il vaut la peine de mesurer leur impact :
1. Outils de mesure de la performance
- Lighthouse - un outil d'analyse de performance complet
- Chrome DevTools Performance - une analyse détaillée du chargement des ressources
- WebPageTest - des tests de performance dans diverses conditions
- Core Web Vitals dans la Google Search Console
2. Tests A/B des stratégies de chargement
1'use client';
2
3import { useState, useEffect } from 'react';
4import Image from 'next/image';
5
6export default function ABTestImage({ src, alt }) {
7 const [testGroup, setTestGroup] = useState('');
8
9 useEffect(() => {
10 // Assign user to group A or B
11 const group = Math.random() > 0.5 ? 'A' : 'B';
12 setTestGroup(group);
13
14 // Save données télémétriques
15 const trackImageLoad = (time) => {
16 // Sending data to the analytics system
17 console.log(`Image loaded in ${time}ms (Group ${group})`);
18 };
19
20 const startTime = performance.now();
21 const onLoadComplete = () => {
22 const loadTime = performance.now() - startTime;
23 trackImageLoad(loadTime);
24 };
25
26 // Listen for the image load event
27 const imageEl = document.getElementById('test-image');
28 if (imageEl) {
29 imageEl.addEventListener('load', onLoadComplete);
30 return () => {
31 imageEl.removeEventListener('load', onLoadComplete);
32 };
33 }
34 }, []);
35
36 if (!testGroup) return null;
37
38 return (
39 <div className="relative h-96 w-full">
40 <Image
41 id="test-image"
42 src={src}
43 alt={alt}
44 fill
45 style={{ objectFit: 'cover' }}
46 preload={testGroup === 'A'} // Group A uses preload, group B does not
47 />
48 </div>
49 );
50}Bonnes pratiques pour les stratégies de chargement des images
N'utilise
preloadque pour les images above-the-fold - idéalement pour une seule image LCP par pageUtilise le lazy loading par défaut - cela économise des ressources et accélère le chargement initial de la page
Surveille les métriques Web Vitals - vérifie régulièrement comment les stratégies de chargement affectent la performance
Adapte les stratégies au type de page :
- Pages produit :
preloadpour la photo principale du produit - Pages de catégorie :
lazypour la plupart des images,eagerpour les premières - Page d'accueil :
preloadpour l'image hero,lazypour le reste
- Pages produit :
Prends en compte les appareils des utilisateurs :
- Les utilisateurs mobiles ont souvent des connexions plus lentes - il est d'autant plus important d'utiliser
lazyet des images plus petites - Les utilisateurs desktop peuvent bénéficier de
eagerpour une meilleure expérience de défilement
- Les utilisateurs mobiles ont souvent des connexions plus lentes - il est d'autant plus important d'utiliser
Utilise des placeholders pour une meilleure expérience de chargement :
- Pour les images avec
preload- affiche un placeholderblurouempty - Pour les images avec
lazy- utiliseblurpour une transition fluide
- Pour les images avec
Scénarios pour différentes stratégies
1. Page d'accueil e-commerce
1export default function Homepage() {
2 return (
3 <div>
4 {/* Hero section - priority */}
5 <div className="relative h-[80vh]">
6 <Image
7 src="/images/hero.jpg"
8 alt="Bienvenue sur Quantum Shop"
9 fill
10 style={{ objectFit: 'cover' }}
11 preload
12 />
13 </div>
14
15 {/* Promotions - eager for the first two */}
16 <div className="grid grid-cols-2 md:grid-cols-4 gap-4 my-8">
17 {promotions.map((promo, index) => (
18 <div key={promo.id} className="relative aspect-square">
19 <Image
20 src={promo.image}
21 alt={promo.title}
22 fill
23 style={{ objectFit: 'cover' }}
24 loading={index < 2 ? 'eager' : 'lazy'}
25 />
26 </div>
27 ))}
28 </div>
29
30 {/* Product categories - lazy (default) */}
31 <div className="grid grid-cols-2 md:grid-cols-3 gap-6 my-12">
32 {categories.map(category => (
33 <div key={category.id} className="relative aspect-video">
34 <Image
35 src={category.image}
36 alt={category.name}
37 fill
38 style={{ objectFit: 'cover' }}
39 // default lazy loading
40 />
41 </div>
42 ))}
43 </div>
44 </div>
45 );
46}2. Blog avec de longs articles
1export default function BlogPost({ post }) {
2 return (
3 <article className="max-w-3xl mx-auto py-8">
4 {/* Main article image - priority */}
5 <div className="relative aspect-video mb-8">
6 <Image
7 src={post.featuredImage}
8 alt={post.title}
9 fill
10 style={{ objectFit: 'cover' }}
11 preload
12 />
13 </div>
14
15 <h1 className="text-3xl font-bold mb-4">{post.title}</h1>
16 <div className="prose max-w-none">
17 {post.content.map((block, index) => {
18 if (block.type === 'paragraph') {
19 return <p key={index}>{block.content}</p>;
20 }
21 if (block.type === 'image') {
22 return (
23 <div key={index} className="relative aspect-video my-8">
24 <Image
25 src={block.url}
26 alt={block.caption || ''}
27 fill
28 style={{ objectFit: 'contain' }}
29 // default lazy loading for images in content
30 />
31 {block.caption && (
32 <p className="text-sm text-gray-500 mt-2 text-center">
33 {block.caption}
34 </p>
35 )}
36 </div>
37 );
38 }
39 return null;
40 })}
41 </div>
42
43 {/* Related articles - all lazy */}
44 <div className="mt-12">
45 <h2 className="text-2xl font-bold mb-6">Related Articles</h2>
46 <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
47 {post.relatedPosts.map(relatedPost => (
48 <div key={relatedPost.id} className="relative aspect-video">
49 <Image
50 src={relatedPost.thumbnail}
51 alt={relatedPost.title}
52 fill
53 style={{ objectFit: 'cover' }}
54 // All lazy (default)
55 />
56 </div>
57 ))}
58 </div>
59 </div>
60 </article>
61 );
62}Résumé
Le choix de la bonne stratégie de chargement des images est crucial pour optimiser la performance et l'expérience utilisateur. Tout comme dans le système de transport de Metropolis Quantum, où les véhicules de priorités différentes se déplacent avec une urgence variable, dans une application web les images peuvent être chargées avec différentes stratégies selon leur importance :
Le priority loading - pour les images « above the fold » les plus importantes, qui ont un impact direct sur le LCP et la première impression de l'utilisateur
L'eager loading - pour les images moins critiques mais tout de même importantes, qui seront probablement nécessaires peu après le chargement de la page
Le lazy loading - pour la plupart des images, en particulier celles sous la ligne de flottaison, qui ne seront chargées que lorsque l'utilisateur les atteindra
Lorsque tu choisis une stratégie de chargement pour tes images, prends en compte :
- La visibilité de l'image sans défilement
- L'impact sur les métriques de performance clés
- Le type de contenu et son importance
- Les appareils et connexions internet de tes utilisateurs
En appliquant correctement les stratégies de chargement des images, tu peux créer une application qui se charge vite, gère efficacement ses ressources et offre une expérience utilisateur fluide - tout comme le système de transport intelligent de Metropolis Quantum, qui assure une circulation fluide des véhicules de priorités différentes.
Dans le prochain chapitre, nous nous concentrerons sur l'optimisation des polices et le composant Font, ce qui nous permettra d'améliorer encore la performance et l'esthétique de notre application Next.js 16.
Code de cette leçon : app/page.tsx
1// Image Loading Strategies - lazy, eager, priority
2'use client';
3
4import { useState } from 'react';
5
6export default function LoadingStrategiesDemo() {
7 const [activeStrategy, setActiveStrategy] = useState('lazy');
8
9 const strategies = [
10 {
11 id: 'lazy',
12 name: 'Lazy Loading',
13 icon: '',
14 color: 'blue',
15 description: 'Default strategy. Images load only when approaching the viewport.',
16 code: `<Image
17 src="/photo.jpg"
18 alt="Gallery image"
19 width={800}
20 height={600}
21 loading="lazy" // default
22/>`,
23 useCase: 'Below-the-fold images, galleries, blog content images',
24 pros: ['Saves bandwidth', 'Faster initial page load', 'Default behavior'],
25 cons: ['Slight delay when scrolling to image', 'Not ideal for hero images'],
26 },
27 {
28 id: 'eager',
29 name: 'Eager Loading',
30 icon: '',
31 color: 'green',
32 description: 'Images load immediately, regardless of position on page.',
33 code: `<Image
34 src="/photo.jpg"
35 alt="Important image"
36 width={800}
37 height={600}
38 loading="eager"
39/>`,
40 useCase: 'Images likely to be seen quickly, carousels, critical UI elements',
41 pros: ['No loading delay', 'Good for near-viewport images'],
42 cons: ['Uses more bandwidth', 'Can slow initial load'],
43 },
44 {
45 id: 'priority',
46 name: 'Priority Loading',
47 icon: '',
48 color: 'purple',
49 description: 'Highest priority loading. Preloaded in <head> for fastest display.',
50 code: `<Image
51 src="/hero.jpg"
52 alt="Hero banner"
53 width={1920}
54 height={1080}
55 priority // preloads in <head>
56/>`,
57 useCase: 'Hero images, LCP elements, above-the-fold content',
58 pros: ['Fastest loading', 'Improves LCP score', 'Preloaded in head'],
59 cons: ['Use sparingly (1-2 per page)', 'Increases initial payload'],
60 },
61 ];
62
63 const active = strategies.find(s => s.id === activeStrategy);
64
65 return (
66 <div className="min-h-screen bg-gray-900 text-white p-8">
67 <div className="max-w-4xl mx-auto">
68 <h1 className="text-3xl font-bold mb-2">Image Loading Strategies</h1>
69 <p className="text-gray-400 mb-8">
70 Choose the right strategy for each image in your Next.js app
71 </p>
72
73 {/* Strategy Tabs */}
74 <div className="flex gap-4 mb-8">
75 {strategies.map(s => (
76 <button
77 key={s.id}
78 onClick={() => setActiveStrategy(s.id)}
79 className={`px-6 py-3 rounded-lg font-medium transition-colors ${
80 activeStrategy === s.id
81 ? 'bg-blue-600 text-white'
82 : 'bg-gray-800 text-gray-400 hover:bg-gray-700'
83 }`}
84 >
85 {s.icon} {s.name}
86 </button>
87 ))}
88 </div>
89
90 {/* Active Strategy Details */}
91 {active && (
92 <div className="bg-gray-800 rounded-xl p-8">
93 <h2 className="text-2xl font-bold mb-2">
94 {active.icon} {active.name}
95 </h2>
96 <p className="text-gray-400 mb-6">{active.description}</p>
97
98 {/* Code Example */}
99 <div className="bg-gray-900 rounded-lg p-4 mb-6">
100 <pre className="text-sm text-green-400 overflow-x-auto">
101 {active.code}
102 </pre>
103 </div>
104
105 {/* Use Cases */}
106 <div className="mb-6">
107 <h3 className="font-semibold mb-2">When to use:</h3>
108 <p className="text-gray-400">{active.useCase}</p>
109 </div>
110
111 {/* Pros & Cons */}
112 <div className="grid grid-cols-2 gap-4">
113 <div>
114 <h3 className="font-semibold text-green-400 mb-2">Pros</h3>
115 <ul className="space-y-1">
116 {active.pros.map((p, i) => (
117 <li key={i} className="text-sm text-gray-400">• {p}</li>
118 ))}
119 </ul>
120 </div>
121 <div>
122 <h3 className="font-semibold text-red-400 mb-2">Cons</h3>
123 <ul className="space-y-1">
124 {active.cons.map((c, i) => (
125 <li key={i} className="text-sm text-gray-400">• {c}</li>
126 ))}
127 </ul>
128 </div>
129 </div>
130 </div>
131 )}
132
133 {/* Priority Guide */}
134 <div className="mt-8 bg-gray-800 rounded-xl p-6">
135 <h2 className="text-xl font-bold mb-4">Strategy Map</h2>
136 <div className="space-y-3">
137 <div className="flex items-center gap-4 p-3 bg-purple-900/30 rounded-lg">
138 <span className="text-2xl">↯</span>
139 <div>
140 <span className="font-semibold text-purple-400">Priority</span>
141 <span className="text-gray-400 ml-2">→ Hero image, main banner (1-2 per page)</span>
142 </div>
143 </div>
144 <div className="flex items-center gap-4 p-3 bg-green-900/30 rounded-lg">
145 <span className="text-2xl">»</span>
146 <div>
147 <span className="font-semibold text-green-400">Eager</span>
148 <span className="text-gray-400 ml-2">→ Near-viewport images, carousel slides</span>
149 </div>
150 </div>
151 <div className="flex items-center gap-4 p-3 bg-blue-900/30 rounded-lg">
152 <span className="text-2xl">›</span>
153 <div>
154 <span className="font-semibold text-blue-400">Lazy</span>
155 <span className="text-gray-400 ml-2">→ Everything else (default)</span>
156 </div>
157 </div>
158 </div>
159 </div>
160 </div>
161 </div>
162 );
163}Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Styled-components est une bibliothèque pour :
2. Le design mobile-first signifie :
Tâches pratiques dans le jeu
- Éditeur de code
Crée un fichier de variables CSS définissant un design system complet (couleurs, espacements, typographie, ombres)
- Tri vertical
Classe dans le bon ordre les étapes d'utilisation des CSS Modules dans Next.js :
- Clique dans l'ordre
Reconstitue la syntaxe de className avec des classes Tailwind CSS en JSX
- Éditeur de code
Crée une animation keyframes pour un effet fade-in et une transition au survol pour un bouton