
À la fin du chapitre précédent, vous avez externalisé vos données dans des fichiers JSON et créé des pages dynamiques pour vos projets et formations. Votre portfolio fonctionne parfaitement. Mais si vous ouvrez les outils de développement de votre navigateur et regardez l'onglet Network, vous remarquerez que :
Vos images se chargent sans optimisation… Les images Placeholder de placehold.co pèsent quelques kilooctets, mais imaginez avec de vraies photos de projets en haute résolution : plusieurs mégaoctets par image !
Votre police par défaut n'est pas optimisée, ce qui peut créer un flash de texte non stylé lors du chargement.
Dans ce chapitre, vous allez découvrir deux fonctionnalités puissantes de Next.js qui vont transformer les performances de votre portfolio :
Image pour l'optimisation automatique des images,
next/font pour des polices ultra-rapides.
Actuellement, votre code utilise la balise HTML standard <img> :
<img
src={project.image}
alt={project.title}
className={styles.image}
/>Cette approche fonctionne, mais elle présente plusieurs problèmes de performance :
Taille non optimisée : L'image est chargée dans sa taille originale, même si elle est affichée en plus petit. Une image de 3000x2000 pixels sera téléchargée même si elle n'est affichée qu'en 400x300.
Format non adapté : L'image est servie dans son format d'origine (JPG, PNG), alors que des formats modernes comme WebP sont 30% plus légers.
Chargement non optimisé : Toutes les images se chargent immédiatement, même celles qui ne sont pas visibles à l'écran.
Cumulative Layout Shift (CLS) : Sans dimensions définies, la page "saute" quand l'image se charge, ce qui est agaçant pour l'utilisateur.
C'est exactement ce que le composant Image de Next.js résout automatiquement !
Le composant Image est une version améliorée de <img> qui optimise automatiquement vos images. Modifiez app/projets/page.js :
import Link from 'next/link'
import Image from 'next/image'
import styles from './page.module.css'
import projectsData from '@/data/projects.json'
import Tag from '@/components/Tag/Tag'
export default function Projects() {
return (
<div className={styles.container}>
{/* …… */}
<Image
src={project.image}
alt={project.title}
width={800}
height={500}
className={styles.image}
/>
{/* …… */}
</div>
)
}Il y a deux changements importants :
Import du composant : import Image from 'next/image' au lieu d'utiliser <img>
Propriétés width et height : Elles sont obligatoires ! Next.js en a besoin pour calculer le ratio d'aspect et éviter le layout shift. Ces valeurs correspondent aux dimensions originales de l'image (ici 800x500 pour nos placeholders), pas à la taille d'affichage finale.
Faites la même modification dans app/projets/[slug]/page.js :
import Link from 'next/link'
import Image from 'next/image'
import { notFound } from 'next/navigation'
import Tag from './../../../components/Tag/Tag'
import styles from './page.module.css'
import projectsData from '@/data/projects.json'
export default async function ProjectDetail({ params }) {
const { slug } = await params
const project = projectsData.find(p => p.slug === slug)
if (!project) {
notFound()
}
return (
<div className={styles.container}>
{/* … */}
<Image
src={project.image}
alt={project.title}
width={800}
height={500}
className={styles.image}
preload={true}
/>
</div>
{/* … */}
)
}Remarquez l'ajout de la propriété preload={true} sur l'image de détail. Cette prop indique à Next.js que cette image est importante et doit être chargée en priorité, car elle est visible immédiatement quand l'utilisateur arrive sur la page.
Avant de tester votre code il vous faudra faire un dernier changement si vous utilisez comme moi des images qui proviennent de sources externes. Allez dans next.config.mjs et modifiez cela :
/** @type {import('next').NextConfig} */
const nextConfig = {
/* config options here */
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'placehold.co',
},
{
protocol: 'https',
hostname: 'oc-static.imgix.net',
},
],
},
};
export default nextConfig;Ici nous ajoutons à la configuration de Next les domaines que nous autorisons à charger des images sur notre site. Ici nous avons laissé un remote pattern simple mais il existe plusieurs options pour ces patterns.
Testez : Rechargez vos pages de projets. Visuellement, rien ne change, mais si vous ouvrez l'inspecteur et regardez les requêtes réseau, vous verrez que Next.js a automatiquement optimisé les images !
Pour un portfolio professionnel, vous voudrez utiliser vos propres images plutôt que des placeholders. Créez un dossier public/images/projets à la racine de votre projet.
Le dossier public est spécial dans Next.js : tout ce qui s'y trouve est accessible publiquement. Par exemple, un fichier public/images/logo.png sera accessible à l'URL /images/logo.png.
Pour l'instant, gardons les placeholders dans le JSON, mais sachez que pour utiliser des images locales, vous modifieriez simplement le chemin dans data/projects.json :
{
"id": 1,
"slug": "taskflow-app",
"title": "TaskFlow",
"image": "/images/taskflow.jpg",
...
}Le composant Image fonctionnerait exactement de la même manière ! La seule différence : Next.js optimiserait automatiquement vos images locales au moment du build, ce qui est encore plus rapide.
Maintenant que vos images sont optimisées, passons aux polices ! Actuellement, votre site utilise les polices par défaut du navigateur. Ajoutons une belle police moderne qui donnera un aspect professionnel à votre portfolio.
Next.js propose next/font , un système qui télécharge les polices au moment du build et les sert directement depuis votre serveur. Résultat : pas de requête externe vers Google Fonts, pas de flash de texte non stylé, et des performances optimales !
Modifiez app/layout.js :
import { Inter, Poppins } from 'next/font/google'
import "./globals.css"
import Navigation from '../components/Navigation/Navigation'
import Footer from '../components/Footer/Footer'
// Configurer Inter pour le texte courant
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
})
// Configurer Poppins pour les titres
const poppins = Poppins({
weight: ['400', '600', '700'],
subsets: ['latin'],
display: 'swap',
variable: '--font-poppins',
})
export const metadata = {
title: "Portfolio d'Aurélien",
description: "Développeur web passionné par React et Next.js",
}
export default function RootLayout({ children }) {
return (
<html lang="fr" className={`${inter.variable} ${poppins.variable}`}>
<body className={inter.className}>
<Navigation />
<main>{children}</main>
<Footer />
</body>
</html>
)
}Que se passe-t-il ici ?
1. Import des polices : next/font/google vous donne accès à toutes les Google Fonts directement dans votre code.
2. Configuration :
subsets: ['latin'] télécharge uniquement les caractères dont vous avez besoin
display: 'swap' affiche le texte avec une police par défaut en attendant le téléchargement (évite le texte invisible)
variable: '--font-inter' crée une variable CSS personnalisée pour utiliser la police
3. Application :
Sur<html>: Les variables CSS sont disponibles partout
Sur<body>: Inter est la police par défaut pour tout le texte
Maintenant, ajoutez les styles pour utiliser Poppins sur les titres. Dans app/globals.css, ajoutez :
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-poppins), sans-serif;
font-weight: 700;
}
body {
font-family: var(--font-inter), sans-serif;
font-weight: 400;
line-height: 1.6;
}Testez : Rechargez votre page, les polices sont maintenant appliquées ! Regardez l'onglet Network de vos outils de développement : les polices sont servies directement depuis votre serveur, pas depuis Google. C'est plus rapide et plus sécurisé !
Vous avez déjà des metadata de base dans votre layout.js, mais chaque page devrait avoir ses propres metadata optimisées pour le SEO. Ajoutons des metadata personnalisées à vos pages de projets.
Modifiez app/projets/page.js:
import Link from 'next/link'
import Image from 'next/image'
import styles from './page.module.css'
import projectsData from '@/data/projects.json'
import Tag from '@/components/Tag/Tag'
export const metadata = {
title: 'Mes Projets | Portfolio',
description: 'Découvrez mes projets de développement web : applications React, sites Next.js et plus encore.',
}
export default function Projects() {
// ... reste du code inchangé
}Pour les pages dynamiques, utilisez generateMetadatadansapp/projets/[slug]/page.js:
import Link from 'next/link'
import Image from 'next/image'
import { notFound } from 'next/navigation'
import Tag from './../../../components/Tag/Tag'
import styles from './page.module.css'
import projectsData from '@/data/projects.json'
export async function generateMetadata({ params }) {
const { slug } = await params
const project = projectsData.find(p => p.slug === slug)
if (!project) {
return {
title: 'Projet non trouvé',
}
}
return {
title: `${project.title} | Portfolio`,
description: project.longDescription,
openGraph: {
title: project.title,
description: project.shortDescription,
images: [project.image],
},
}
}
export default async function ProjectDetail({ params }) {
// ... reste du code inchangé
}
export function generateStaticParams() {
return projectsData.map((project) => ({
slug: project.slug,
}))
}La fonction generateMetadata est appelée automatiquement par Next.js avant de rendre la page. Elle peut être asynchrone et a accès aux mêmes params que votre composant. Ici, nous créons des metadata personnalisées pour chaque projet !
Testez : Visitez une page de projet et affichez le code source (clic droit > Afficher le code source). Vous verrez les balises <meta> dans le <head> avec les informations de votre projet !
Google utilise trois métriques principales pour évaluer la performance d'un site web. Ces métriques s'appellent les Core Web Vitals et influencent directement votre référencement :
LCP (Largest Contentful Paint) : Temps avant que le plus gros élément visible soit affiché. Doit être inférieur à 2,5 secondes.
FID (First Input Delay) : Temps avant que l'utilisateur puisse interagir avec la page. Doit être inférieur à 100 millisecondes.
CLS (Cumulative Layout Shift) : Stabilité visuelle de la page.
La bonne nouvelle ? Next.js optimise automatiquement ces trois métriques avec :
Le composantImagequi évite le CLS en définissant les dimensions
next/fontqui évite le flash de texte (CLS)
La génération statique qui rend le LCP ultra-rapide
L'optimisation du code JavaScript qui améliore le FID

Avant de passer au chapitre suivant, appliquez ce que vous avez appris !
Remplacez les <img> par le composantImagedansapp/formations/[slug]/page.js
Ajoutez des metadata personnalisées pour la page de liste des formations, et chaque page de détail de formation (avec generateMetadata)
Ajoutez des images locales pour au moins une formation dans public/images/formations/
Conseils :
Pour les images de formations sans photo, vous pouvez utiliser des icônes ou des illustrations représentant l'école ou le sujet.
Les dimensions width et heightdoivent correspondre aux dimensions réelles de vos images.
N'oubliez pas d'ajouter un altdescriptif pour l'accessibilité.
Pour les metadata, pensez aux mots-clés pertinents pour le SEO.
Le composant Imagepermet une optimisation automatique des images (format, taille, lazy loading)
Avec next/font, les polices Google Fonts optimisées et servies localement. On peut utiliser des polices via des variables CSS personnalisées.
openGraph permet une optimisation du partage sur les réseaux sociaux.
Avant ces optimisations | Après ces optimisations |
|
|
Dans le prochain chapitre, vous allez déployer votre portfolio en production sur Vercel et le rendre accessible au monde entier !