Cours Vibe coding avec l'IA · Module 4: Clone d'Instagram
Composant Image de Next.js - Optimisation des images
Dans cette leçon12
Bienvenue ! Il est temps de construire Treasure Gallery - une galerie de photos de pirates inspirée d'Instagram.
Avant de commencer à construire la galerie, tu dois apprendre comment Next.js gère les images. Spoiler : bien mieux qu'une simple balise <img> !
Le problème de la balise <img> classique
La balise traditionnelle <img> a plusieurs problèmes :
Aucune optimisation automatique - les fichiers volumineux ralentissent la page Pas de lazy loading - toutes les images se chargent d'un coup Pas de responsivité - une seule taille pour tous les appareils Cumulative Layout Shift (CLS) - la page « saute » quand les images se chargent
Composant Image de Next.js - La solution
Next.js dispose d'un composant <Image> intégré qui automatiquement :
Optimise les images au format WebP/AVIF Lazy loading - ne charge que les images visibles Responsive - livre la bonne taille pour l'appareil Évite le CLS - réserve l'espace avant le chargement
Utilisation de base :
L'exemple le plus simple : tu importes le composant depuis next/image et tu lui donnes un chemin, une description et les dimensions de l'image.
1import Image from 'next/image';
2
3export default function Gallery() {
4 return (
5 <Image
6 src="/treasure-map.jpg"
7 alt="Carte au trésor"
8 width={800}
9 height={600}
10 />
11 );
12}Important : Tu dois fournir width et height - Next.js en a besoin pour l'optimisation.
Images du dossier public/
Les fichiers du dossier public/ sont directement accessibles via une URL.
Structure :
1public/
2├── images/
3│ ├── treasure-1.jpg
4│ ├── treasure-2.jpg
5│ └── pirate-ship.jpg
6└── logo.pngUtilisation :
Tu commences le chemin d'un fichier du dossier public/ par une barre oblique, sans le mot public dans l'adresse.
1<Image
2 src="/images/treasure-1.jpg"
3 alt="Trésor doré"
4 width={600}
5 height={400}
6/>Le chemin commence par / - cela désigne le dossier public/.
Images externes (depuis une URL)
Tu peux aussi charger des images depuis des sources externes, mais tu dois les configurer.
1. Ajoute dans next.config.js :
Next.js n'optimise pas une image d'un domaine étranger tant que tu n'ajoutes pas ce domaine à la liste remotePatterns de la configuration.
1// next.config.js
2module.exports = {
3 images: {
4 remotePatterns: [
5 {
6 protocol: 'https',
7 hostname: 'images.unsplash.com',
8 },
9 {
10 protocol: 'https',
11 hostname: 'i.imgur.com',
12 },
13 ],
14 },
15};2. Utilise l'URL complète :
Ensuite, dans le composant, tu passes l'adresse complète de l'image, et la largeur et la hauteur restent nécessaires.
1<Image
2 src="https://images.unsplash.com/photo-1234567890"
3 alt="Navire pirate"
4 width={800}
5 height={600}
6/>fill - Images qui remplissent un conteneur
Quand tu ne connais pas les dimensions exactes ou que tu veux une image responsive, utilise fill.
1<div className="relative w-full h-96">
2 <Image
3 src="/treasure-map.jpg"
4 alt="Carte"
5 fill
6 className="object-cover"
7 />
8</div>Important :
- Le parent doit avoir
position: relative - Utilise
object-coverouobject-containpour contrôler la mise à l'échelle
object-cover vs object-contain :
Les deux classes décident du sort d'une image dont les proportions diffèrent du conteneur : la rogner ou laisser des zones vides.
1// object-cover - l'image remplit le conteneur, peut être rognée
2<Image
3 src="/wide-image.jpg"
4 alt="Image large"
5 fill
6 className="object-cover"
7/>
8
9// object-contain - l'image entière est visible, peut laisser des espaces vides
10<Image
11 src="/tall-image.jpg"
12 alt="Image haute"
13 fill
14 className="object-contain"
15/>Images importantes - loading="eager" et fetchPriority
Par défaut, Next.js charge les images en différé (lazy loading), c'est-à-dire seulement quand elles approchent de la partie visible de l'écran. Une image visible tout de suite, comme la bannière principale (souvent l'élément LCP), doit se charger plus tôt. Depuis Next.js 16, tu le fais avec loading="eager" et fetchPriority="high" :
1<Image
2 src="/hero-banner.jpg"
3 alt="Bannière principale"
4 width={1200}
5 height={600}
6 loading="eager"
7 fetchPriority="high"
8/>loading="eager" désactive le lazy loading, et fetchPriority="high" indique au navigateur que cette image est plus urgente que les autres. L'ancienne prop priority est dépréciée depuis Next.js 16. Elle a été remplacée par preload, qui insère un <link rel="preload"> dans le <head>, mais la documentation ne la recommande que dans des cas particuliers, et pas avec loading ou fetchPriority.
Placeholder - Effet de flou pendant le chargement
Next.js peut afficher un placeholder flou pendant que l'image se charge :
1<Image
2 src="/treasure.jpg"
3 alt="Trésor"
4 width={600}
5 height={400}
6 placeholder="blur"
7 blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..." // Minuscule image Base64
8/>Ou automatiquement pour les images locales :
1import treasureImage from '@/public/treasure.jpg';
2
3<Image
4 src={treasureImage}
5 alt="Trésor"
6 placeholder="blur"
7/>Exemple pratique - Galerie Instagram
Combinons fill, object-cover et un overlay au survol dans une grille de vignettes carrées, comme sur Instagram.
1import Image from 'next/image';
2
3interface GalleryPost {
4 id: number;
5 imageUrl: string;
6 caption: string;
7 author: string;
8 likes: number;
9}
10
11function GalleryGrid({ posts }: { posts: GalleryPost[] }) {
12 return (
13 <div className="grid grid-cols-3 gap-1">
14 {posts.map((post) => (
15 <div key={post.id} className="relative aspect-square group cursor-pointer">
16 <Image
17 src={post.imageUrl}
18 alt={post.caption}
19 fill
20 className="object-cover"
21 />
22
23 {/* Overlay au survol */}
24 <div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
25 <div className="text-white text-center">
26 <p className="font-bold">{post.likes}</p>
27 <p className="text-sm">{post.caption}</p>
28 </div>
29 </div>
30 </div>
31 ))}
32 </div>
33 );
34}Loading States - Skeleton UI
Affiche un placeholder de chargement pendant que les images se chargent :
1'use client';
2
3import Image from 'next/image';
4import { useState } from 'react';
5
6export default function PhotoCard({ src, alt }: { src: string; alt: string }) {
7 const [isLoading, setIsLoading] = useState(true);
8
9 return (
10 <div className="relative aspect-square">
11 {isLoading && (
12 <div className="absolute inset-0 bg-gray-200 animate-pulse" />
13 )}
14
15 <Image
16 src={src}
17 alt={alt}
18 fill
19 className={`object-cover transition-opacity duration-300 ${
20 isLoading ? 'opacity-0' : 'opacity-100'
21 }`}
22 onLoad={() => setIsLoading(false)}
23 />
24 </div>
25 );
26}Sizes - Tailles responsives
Indique à Next.js quelles tailles d'image tu as besoin pour différents écrans :
1<Image
2 src="/treasure.jpg"
3 alt="Trésor"
4 fill
5 sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
6 className="object-cover"
7/>Signification :
- Mobile (≤768px) : 100% de la largeur du viewport
- Tablette (≤1200px) : 50% de la largeur du viewport
- Desktop : 33% de la largeur du viewport
Quality - Contrôle de la qualité
Par défaut, Next.js compresse les images à quality=75. Depuis Next.js 16, les valeurs de qualité autorisées se déclarent dans images.qualities de next.config (seulement [75] par défaut), et toute autre valeur est arrondie à la valeur autorisée la plus proche. Pour utiliser quality={90}, ajoute 90 à la liste :
1// next.config.ts
2images: {
3 qualities: [75, 90],
4},1<Image
2 src="/high-quality-art.jpg"
3 alt="Œuvre d'art"
4 width={1200}
5 height={800}
6 quality={90} // Qualité supérieure (fichier plus lourd), fonctionne une fois 90 ajouté à images.qualities
7/>Résumé
Next.js Image - optimisation automatique width/height - obligatoires pour les images locales fill - images responsives dans un conteneur loading="eager" / fetchPriority="high" - chargement prioritaire des images importantes (à la place de priority, dépréciée) placeholder="blur" - effet de flou pendant le chargement object-cover - l'image remplit le conteneur object-contain - l'image entière est visible remotePatterns - configuration pour les sources externes sizes - tailles responsives pour l'optimisation
Dans le prochain exercice, nous construirons une grille de galerie avec des modales !
À bientôt !
Code de cette leçon : App.jsx
1// Démo d'optimisation d'image avec Next.js Image
2export default function App() {
3 return (
4 <div className="min-h-screen bg-gradient-to-br from-yellow-900 to-amber-700 p-8">
5 <div className="max-w-4xl mx-auto">
6 <h1 className="text-4xl font-bold text-white text-center mb-8">
7 Trésors de pirates
8 </h1>
9
10 <div className="grid grid-cols-2 gap-4">
11 {/* Trésor 1 */}
12 <div className="bg-white rounded-lg overflow-hidden shadow-xl">
13 <div className="relative aspect-square bg-gradient-to-br from-yellow-400 to-amber-600 flex items-center justify-center">
14 <div className="text-8xl">◆</div>
15 </div>
16 <div className="p-4">
17 <h3 className="font-bold text-lg">Pièces d'or</h3>
18 <p className="text-gray-600 text-sm">Trouvées dans un coffre</p>
19 </div>
20 </div>
21
22 {/* Trésor 2 */}
23 <div className="bg-white rounded-lg overflow-hidden shadow-xl">
24 <div className="relative aspect-square bg-gradient-to-br from-blue-400 to-cyan-600 flex items-center justify-center">
25 <div className="text-8xl">◆</div>
26 </div>
27 <div className="p-4">
28 <h3 className="font-bold text-lg">Diamant royal</h3>
29 <p className="text-gray-600 text-sm">Trésor de l'épave</p>
30 </div>
31 </div>
32
33 {/* Trésor 3 */}
34 <div className="bg-white rounded-lg overflow-hidden shadow-xl">
35 <div className="relative aspect-square bg-gradient-to-br from-purple-400 to-pink-600 flex items-center justify-center">
36 <div className="text-8xl">◆</div>
37 </div>
38 <div className="p-4">
39 <h3 className="font-bold text-lg">Couronne de pirate</h3>
40 <p className="text-gray-600 text-sm">Gagnée au combat</p>
41 </div>
42 </div>
43
44 {/* Trésor 4 */}
45 <div className="bg-white rounded-lg overflow-hidden shadow-xl">
46 <div className="relative aspect-square bg-gradient-to-br from-red-400 to-orange-600 flex items-center justify-center">
47 <div className="text-8xl">◆</div>
48 </div>
49 <div className="p-4">
50 <h3 className="font-bold text-lg">Carte au trésor</h3>
51 <p className="text-gray-600 text-sm">Mène à l'île</p>
52 </div>
53 </div>
54 </div>
55 </div>
56 </div>
57 );
58}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. Pourquoi <Image> de Next.js est-il meilleur qu'un <img> classique ?
2. Vers quel format le composant Image de Next.js optimise-t-il automatiquement les images ?
Voici 2 des 7 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Clique dans l'ordre
Clique sur les éléments dans l'ordre pour importer le composant Image depuis Next.js :
- Tri horizontal
Ordonne les éléments de l'instruction d'import du composant Image de Next.js :
- Éditeur de code
Composant Image
- Clique dans l'ordre
Clique sur les éléments dans l'ordre pour construire l'attribut fill de l'image avec la classe object-cover :
- Tri vertical
Ordonne les lignes d'un composant Image qui remplit son conteneur (de haut en bas) :
- Éditeur de code
Image responsive
- Tri horizontal
Ordonne les fragments du chemin pour l'image treasure-1.jpg située dans public/images :