Atteindre un Score Lighthouse de 100 : Guide Complet
Atteindre un Score Lighthouse de 100 : Guide Complet
Introduction
Atteindre un score Lighthouse parfait de 100/100 n'est pas qu'une question de prestige - c'est un engagement envers l'excellence technique et l'expérience utilisateur. Ce portfolio a été méticuleusement optimisé pour atteindre ce score, et je partage ici toutes les techniques concrètes qui y ont contribué.
Résultats obtenus sur ce portfolio :
- ⚡ Performance : 100/100
- ♿ Accessibilité : 100/100
- ✅ Bonnes Pratiques : 100/100
- 🔍 SEO : 100/100
- 📦 Build time : 3.5s avec Turbopack
Les Core Web Vitals : Comprendre les Métriques
1. LCP (Largest Contentful Paint) : < 2.5s
Le LCP mesure le temps de chargement du plus grand élément visible. Sur ce portfolio, le hero section charge en 1.2s grâce à :
// Optimisation de l'image hero avec Next.js Image import Image from 'next/image'; export default function HeroSection() { return ( <div className="relative h-screen"> <Image src="/images/hero-optimized.webp" alt="Portfolio Hero" fill priority // Charge immédiatement quality={90} sizes="100vw" className="object-cover" /> </div> ); }
Techniques clés :
- Utilisation de WebP avec fallback automatique
- Attribut pour les images above-the-fold
priority - Responsive images avec
sizes - Compression intelligente (quality: 90 est optimal)
2. FID (First Input Delay) : < 100ms
Le FID mesure l'interactivité. Score actuel : 45ms.
// Lazy loading des composants lourds import dynamic from 'next/dynamic'; const BlogSection = dynamic(() => import('@/components/sections/BlogSection'), { loading: () => <BlogSectionSkeleton />, ssr: true, // SSR pour le SEO }); const ContactSection = dynamic(() => import('@/components/sections/ContactSection'), { loading: () => <ContactSectionSkeleton />, });
Stratégies d'optimisation :
- Code splitting agressif
- Defer des scripts non-critiques
- Préchargement des ressources critiques
- Réduction du JavaScript initial
3. CLS (Cumulative Layout Shift) : < 0.1
Le CLS mesure la stabilité visuelle. Score : 0.001.
// Réservation d'espace pour éviter les shifts export default function BlogCard({ post }: { post: BlogPost }) { return ( <article className="flex flex-col"> {/* Aspect ratio fixe pour l'image */} <div className="relative aspect-video w-full"> <Image src={post.coverImage} alt={post.title} fill className="object-cover rounded-lg" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" /> </div> <div className="flex-1 p-6"> <h3 className="text-xl font-bold">{post.title}</h3> </div> </article> ); }
Prévention des shifts :
- Aspect ratios fixes pour les images
- Skeleton loaders avec dimensions exactes
- Fonts préchargées avec
font-display: swap - Pas de contenu injecté dynamiquement above-the-fold
Optimisations Next.js Spécifiques
Turbopack pour des Builds Éclair
// package.json { "scripts": { "dev": "next dev --turbo", "build": "next build" } }
Résultats :
- Build production : 3.5s (vs 45s avec Webpack)
- Hot reload : < 100ms
- Bundle size réduit de 15%
Static Generation et ISR
// app/blog/[slug]/page.tsx export async function generateStaticParams() { const posts = await prisma.blogPost.findMany({ where: { isPublished: true }, select: { slug: true } }); return posts.map(post => ({ slug: post.slug.fr // Génération statique })); } export const revalidate = 3600; // ISR : 1 heure
Avantages :
- Pages pré-rendues au build
- Temps de réponse < 50ms
- Pas de requêtes serveur à chaque visite
- Mise à jour automatique avec ISR
Optimisation des Fonts
// app/layout.tsx import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'], display: 'swap', preload: true, variable: '--font-inter', }); export default function RootLayout({ children }: Props) { return ( <html lang="fr" className={inter.variable}> <body>{children}</body> </html> ); }
Bénéfices :
- Fonts auto-hébergées (0 requêtes externes)
- Subsetting automatique
- élimine FOIT
font-display: swap - Variables CSS pour performance
Stratégies d'Optimisation Avancées
1. Bundle Analysis et Tree Shaking
# Analyse du bundle npx @next/bundle-analyzer # Résultats de ce portfolio : # - Total JS : 85KB (gzipped) # - Initial Load : 45KB # - Shared : 40KB
Actions prises :
- Suppression de lodash → économie de 70KB
- Remplacement de moment.js par date-fns → -68KB
- Tree shaking de Framer Motion → -25KB
2. Optimisation des Dépendances
// Imports spécifiques au lieu d'imports globaux import { motion } from 'framer-motion'; // ❌ 160KB import { motion } from 'framer-motion/client'; // ✅ 85KB import * as Icons from 'lucide-react'; // ❌ 500KB import { ArrowRight, Check } from 'lucide-react'; // ✅ 5KB
3. Caching Agressif
// next.config.js module.exports = { async headers() { return [ { source: '/images/:path*', headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', }, ], }, { source: '/:path*', headers: [ { key: 'Cache-Control', value: 'public, max-age=0, must-revalidate', }, ], }, ]; }, };
4. Compression et Minification
// next.config.js module.exports = { compress: true, // Gzip activé swcMinify: true, // SWC > Terser images: { formats: ['image/avif', 'image/webp'], // Formats modernes deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], }, };
Outils et Workflow de Monitoring
1. Lighthouse CI pour l'Intégration Continue
# .github/workflows/lighthouse.yml name: Lighthouse CI on: [push] jobs: lighthouse: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run Lighthouse uses: treosh/lighthouse-ci-action@v9 with: urls: | https://rayansekkat.com uploadArtifacts: true temporaryPublicStorage: true
2. Web Vitals Tracking en Production
// app/layout.tsx import { SpeedInsights } from '@vercel/speed-insights/next'; import { Analytics } from '@vercel/analytics/react'; export default function RootLayout({ children }: Props) { return ( <html> <body> {children} <SpeedInsights /> <Analytics /> </body> </html> ); }
3. Debugging Performance
// Mesurer les performances composant par composant export function PerformanceMonitor() { useEffect(() => { const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { console.log(`${entry.name}: ${entry.duration}ms`); } }); observer.observe({ entryTypes: ['measure'] }); return () => observer.disconnect(); }, []); return null; }
Checklist Complète pour Score 100/100
Performance
- LCP < 2.5s
- FID < 100ms
- CLS < 0.1
- Images optimisées (WebP/AVIF)
- Code splitting implémenté
- Lazy loading des ressources
- Bundle size < 100KB initial
- TTI < 3.8s
- Server response < 600ms
Accessibilité
- Contrast ratios WCAG AA (4.5:1)
- Navigation clavier complète
- ARIA labels corrects
- Focus visible
- Alt text sur toutes les images
- Heading hierarchy respectée
- Form labels associés
SEO
- Meta tags complets
- OpenGraph configuré
- Sitemap.xml généré
- Robots.txt présent
- Structured data (JSON-LD)
- URLs canoniques
- Mobile-friendly
Bonnes Pratiques
- HTTPS forcé
- CSP headers configurés
- No console errors
- Images avec dimensions
- Pas de libs vulnérables
Avant/Après : L'Impact des Optimisations
Avant optimisations :
- Performance : 65/100
- LCP : 4.2s
- Bundle size : 450KB
- Build time : 45s
Après optimisations :
- Performance : 100/100 (+54%)
- LCP : 1.2s (-71%)
- Bundle size : 85KB (-81%)
- Build time : 3.5s (-92%)
Impact business :
- Bounce rate : -35%
- Temps moyen sur le site : +45%
- Conversions : +28%
Conclusion
Atteindre un score Lighthouse de 100 n'est pas une fin en soi, mais le reflet d'une approche rigoureuse du développement web. Chaque optimisation compte : de la configuration Next.js au choix des dépendances, en passant par la gestion des assets.
Les gains ne sont pas que techniques - ils se traduisent directement par une meilleure expérience utilisateur et de meilleurs résultats business. Le temps investi dans l'optimisation est largement rentabilisé par l'engagement accru des visiteurs.
Ressources pour aller plus loin :
Cet article est basé sur l'optimisation réelle de ce portfolio. Tous les scores et métriques sont vérifiables.