Next.js Performans Optimizasyonu

Lighthouse Skoru 100 Yapmanın Yolları - 2025 Kapsamlı Rehberi

📅 11 Kasım 2025
⏱️ 12 dakika okuma
🏷️ Web Performance
🎯 İleri Seviye
🚀 Hedef: Lighthouse Skoru 100/100 🚀

🚀 Next.js Performans Optimizasyonuna Giriş

Next.js ile geliştirdiğiniz projede Lighthouse skoru 100 almak sadece bir başarı değil, aynı zamanda kullanıcı deneyimi, SEO ve iş sonuçlarınızı doğrudan etkileyen kritik bir faktördür. Google'ın Core Web Vitals güncellemesi ile performans, arama sonuçlarında sıralama faktörü haline geldi.

⚡ Performance (Performans)

Sayfa yükleme hızı, First Contentful Paint ve Largest Contentful Paint optimizasyonu

♿ Accessibility (Erişilebilirlik)

ARIA etiketleri, kontrast oranları ve klavye navigasyonu optimizasyonu

🔍 SEO

Meta tags, structured data ve semantic HTML optimizasyonu

💯 Best Practices

Modern web standartları, güvenlik ve kod kalitesi iyileştirmeleri

📊 Lighthouse Analizi ve Core Web Vitals

Performans optimizasyonuna başlamadan önce mevcut durumu analiz etmemiz gerekiyor. Core Web Vitals metrikleri Google tarafından kullanıcı deneyimini ölçmek için kullanılan temel göstergelerdir.

Core Web Vitals Metrikleri

LCP (Largest Contentful Paint)

< 2.5s

En büyük içerik elementinin yüklenme süresi

FID (First Input Delay)

< 100ms

İlk kullanıcı etkileşiminin gecikme süresi

CLS (Cumulative Layout Shift)

< 0.1

Sayfa yükleme sırasındaki layout kaymaları

FCP (First Contentful Paint)

< 1.8s

İlk içeriğin ekranda görünme süresi

next.config.js - Performans Optimizasyonları
/** @type {import('next').NextConfig} */ const nextConfig = { // Performans optimizasyonları swcMinify: true, compiler: { removeConsole: process.env.NODE_ENV === 'production', }, experimental: { optimizeCss: true, optimizePackageImports: ['@mui/material', 'lodash', 'date-fns'], }, // Görsel optimizasyonu images: { domains: ['example.com', 'cdn.example.com'], formats: ['image/webp', 'image/avif'], minimumCacheTTL: 60 * 60 * 24 * 365, // 1 yıl deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], }, // Compression compress: true, // Headers optimizasyonu async headers() { return [ { source: '/(.*)', headers: [ { key: 'X-Frame-Options', value: 'DENY', }, { key: 'X-Content-Type-Options', value: 'nosniff', }, { key: 'Referrer-Policy', value: 'origin-when-cross-origin', }, ], }, // Static assets için cache headers { source: '/(.*)\\.(js|css|woff|woff2|eot|ttf|otf)$', headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', }, ], }, // Images için cache headers { source: '/(.*)\\.(jpg|jpeg|png|gif|webp|avif|ico|svg)$', headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', }, ], }, ] }, // Webpack optimizasyonu webpack: (config, { isServer, dev }) => { if (!dev && !isServer) { // Production client-side optimizasyonları config.resolve.fallback = { fs: false, net: false, tls: false, } // Tree shaking optimizasyonu config.optimization.usedExports = true config.optimization.sideEffects = false } return config }, } module.exports = nextConfig

🖼️ Görsel ve Media Optimizasyonu

Görseller web sitelerinin büyük kısmını oluşturur. Next.js'in Image bileşeni ile görselleri otomatik optimize edebilir, modern formatlar kullanabilir ve lazy loading uygulayabilirsiniz.

components/OptimizedImage.tsx
import Image from 'next/image' import { useState } from 'react' interface OptimizedImageProps { src: string alt: string width: number height: number priority?: boolean className?: string placeholder?: 'blur' | 'empty' blurDataURL?: string sizes?: string } export default function OptimizedImage({ src, alt, width, height, priority = false, className = '', placeholder = 'blur', blurDataURL, sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw' }: OptimizedImageProps) { const [isLoading, setIsLoading] = useState(true) // Shimmer efekti için SVG const shimmer = (w: number, h: number) => ` ` const toBase64 = (str: string) => typeof window === 'undefined' ? Buffer.from(str).toString('base64') : window.btoa(str) return (
{alt} setIsLoading(false)} quality={85} // Optimal kalite ayarı />
) } // Kullanım örneği - Hero Section export function HeroSection() { return (

Next.js Performans

) }

Modern Görsel Format Desteği

utils/imageUtils.ts
// WebP ve AVIF desteği kontrolü export function checkWebPSupport(): Promise { return new Promise((resolve) => { const webP = new Image() webP.onload = webP.onerror = () => { resolve(webP.height === 2) } webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA' }) } export function checkAVIFSupport(): Promise { return new Promise((resolve) => { const avif = new Image() avif.onload = avif.onerror = () => { resolve(avif.height === 2) } avif.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaW5mZQAAAAAAAAABAAA' }) } // Responsive image srcset oluştur export function generateResponsiveSrcSet(imagePath: string, sizes: number[]): string { return sizes .map(size => `${imagePath}?w=${size}&q=85 ${size}w`) .join(', ') }

🚀 Kod Bölme ve Lazy Loading

Next.js otomatik kod bölme yapar, ancak daha detaylı optimizasyonlar yaparak bundle boyutunu minimize edebilir ve sayfa yükleme performansını artırabilirsiniz.

components/LazyComponents.tsx
import { lazy, Suspense } from 'react' import dynamic from 'next/dynamic' // React.lazy ile kod bölme const HeavyChart = lazy(() => import('./HeavyChart')) const DataTable = lazy(() => import('./DataTable')) // Next.js dynamic import (önerilen) const DynamicChart = dynamic(() => import('./HeavyChart'), { loading: () =>
, ssr: false // Client-side'da yükle }) const DynamicModal = dynamic(() => import('./Modal'), { loading: () =>
Modal yükleniyor...
}) // Conditional loading - sadece gerektiğinde yükle const ConditionalComponent = dynamic( () => import('./ConditionalFeature'), { ssr: false } ) export function DashboardPage() { const [showChart, setShowChart] = React.useState(false) const [showModal, setShowModal] = React.useState(false) return (

Dashboard

{/* Temel içerik hemen yüklenir */}

Ana dashboard içeriği

{/* Chart sadece gerektiğinde yüklenir */} {showChart && ( }> )} {/* Modal sadece açıldığında yüklenir */} {showModal && ( setShowModal(false)} /> )}
) } // Skeleton loading component function ChartSkeleton() { return (
) }

🎨 CSS ve Font Optimizasyonu

CSS ve font optimizasyonları sayfa yükleme hızını önemli ölçüde etkileyebilir. Critical CSS, font loading stratejileri ve CSS minimizasyonu ile performansı artırabilirsiniz.

next.config.js - CSS Optimizasyonu
const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }) /** @type {import('next').NextConfig} */ const nextConfig = { // CSS optimizasyonu experimental: { optimizeCss: true, }, // Font optimizasyonu optimizeFonts: true, webpack: (config, { isServer }) => { // CSS ve SCSS optimizasyonu if (!isServer) { config.resolve.fallback = { fs: false, net: false, tls: false, } } // CSS kritik path optimizasyonu config.optimization.splitChunks = { chunks: 'all', cacheGroups: { default: false, vendors: false, // CSS için ayrı chunk styles: { name: 'styles', type: 'css/mini-extract', chunks: 'all', enforce: true, }, }, } return config }, } module.exports = withBundleAnalyzer(nextConfig)

Font Loading Optimizasyonu

components/FontOptimization.tsx
import { Inter, Roboto_Mono } from 'next/font/google' import localFont from 'next/font/local' // Google Fonts optimizasyonu const inter = Inter({ subsets: ['latin'], display: 'swap', // Font yükleme stratejisi preload: true, variable: '--font-inter', }) const robotoMono = Roboto_Mono({ subsets: ['latin'], display: 'swap', variable: '--font-roboto-mono', weight: ['400', '700'], // Sadece gerekli ağırlıkları yükle }) // Local font optimizasyonu const customFont = localFont({ src: [ { path: '../public/fonts/custom-regular.woff2', weight: '400', style: 'normal', }, { path: '../public/fonts/custom-bold.woff2', weight: '700', style: 'normal', }, ], display: 'swap', variable: '--font-custom', }) // CSS Variables ile font kullanımı export function OptimizedLayout({ children }: { children: React.ReactNode }) { return (
{children}
) }

⚡ SSR ve ISR Performans İyileştirmeleri

Server-Side Rendering (SSR) ve Incremental Static Regeneration (ISR) özelliklerini optimize ederek hem SEO'yu hem de performansı artırabilirsiniz.

pages/optimized-page.tsx
import { GetStaticProps, GetStaticPaths, GetServerSideProps } from 'next' import { unstable_cache } from 'next/cache' // ISR ile statik sayfa oluşturma export default function OptimizedPage({ data, timestamp }: PageProps) { return (

Optimize Edilmiş Sayfa

Son güncelleme: {new Date(timestamp).toLocaleString()}

{data.content}
) } // ISR - Her 60 saniyede bir yeniden oluştur export const getStaticProps: GetStaticProps = async (context) => { try { // Cache'li data fetching const data = await getCachedData() return { props: { data, timestamp: Date.now(), }, revalidate: 60, // 60 saniye ISR } } catch (error) { return { notFound: true, } } } // Cache wrapper function const getCachedData = unstable_cache( async () => { // Expensive data operation const response = await fetch('https://api.example.com/data') if (!response.ok) throw new Error('Failed to fetch data') return response.json() }, ['page-data'], // Cache key { revalidate: 300, // 5 dakika cache tags: ['page-data'], // Cache tag for invalidation } ) // Dynamic pages için optimized paths export const getStaticPaths: GetStaticPaths = async () => { // Sadece popüler sayfaları pre-generate et const popularPaths = await getPopularPaths() return { paths: popularPaths.map((slug) => ({ params: { slug }, })), fallback: 'blocking', // Diğer sayfalar on-demand generate } } // Server-side rendering optimizasyonu export const getServerSideProps: GetServerSideProps = async (context) => { const { req, res } = context // Cache headers res.setHeader( 'Cache-Control', 'public, s-maxage=60, stale-while-revalidate=300' ) // User-agent based optimization const isMobile = req.headers['user-agent']?.includes('Mobile') try { const data = await fetchDataWithTimeout(5000) // 5s timeout return { props: { data, isMobile, }, } } catch (error) { return { notFound: true, } } } // Timeout wrapper async function fetchDataWithTimeout(timeout: number) { const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), timeout) try { const response = await fetch('https://api.example.com/data', { signal: controller.signal, }) clearTimeout(timeoutId) return response.json() } catch (error) { clearTimeout(timeoutId) throw error } }

🗄️ Caching Stratejileri

Etkili caching stratejileri ile tekrarlı istekleri minimize ederek performansı önemli ölçüde artırabilirsiniz.

Önce: Caching Yok

3.2s

Her istek backend'e gidiyor

Sonra: Optimize Cache

0.8s

%75 daha hızlı yükleme

utils/cacheStrategies.ts
// Redis cache implementation import Redis from 'ioredis' const redis = new Redis(process.env.REDIS_URL) export class CacheManager { private static instance: CacheManager private redis: Redis constructor() { this.redis = redis } static getInstance() { if (!CacheManager.instance) { CacheManager.instance = new CacheManager() } return CacheManager.instance } // Cache with TTL async set(key: string, value: any, ttl: number = 3600): Promise { await this.redis.setex(key, ttl, JSON.stringify(value)) } // Get from cache async get(key: string): Promise { const value = await this.redis.get(key) return value ? JSON.parse(value) : null } // Cache with refresh strategy async getOrSet( key: string, fetchFunction: () => Promise, ttl: number = 3600 ): Promise { const cached = await this.get(key) if (cached) { // Background refresh for stale data if (Math.random() < 0.1) { // 10% chance this.refreshInBackground(key, fetchFunction, ttl) } return cached } const fresh = await fetchFunction() await this.set(key, fresh, ttl) return fresh } private async refreshInBackground( key: string, fetchFunction: () => Promise, ttl: number ) { try { const fresh = await fetchFunction() await this.set(key, fresh, ttl) } catch (error) { console.error('Background refresh failed:', error) } } // Invalidate cache async invalidate(pattern: string): Promise { const keys = await this.redis.keys(pattern) if (keys.length > 0) { await this.redis.del(...keys) } } } // SWR (Stale While Revalidate) strategy export function useCachedSWR( key: string, fetcher: () => Promise, options = { revalidateOnFocus: false, refreshInterval: 300000 } ) { return useSWR(key, fetcher, options) }

📈 Performans İzleme ve Analiz

Performans optimizasyonlarının etkisini ölçmek ve sürekli iyileştirme yapmak için kapsamlı izleme sistemi kurmak kritik önemdedir.

utils/performanceMonitoring.ts
// Web Vitals izleme export function reportWebVitals(metric: any) { // Google Analytics 4 entegrasyonu if (window.gtag) { window.gtag('event', metric.name, { custom_parameter_1: Math.round(metric.value), custom_parameter_2: metric.id, custom_parameter_3: metric.name, }) } // Console logging (development) if (process.env.NODE_ENV === 'development') { console.log('Web Vital:', metric) } // Custom analytics endpoint fetch('/api/analytics/web-vitals', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: metric.name, value: metric.value, id: metric.id, url: window.location.pathname, timestamp: Date.now(), }), }).catch(console.error) } // Performance Observer export function initPerformanceObserver() { if (typeof window === 'undefined') return // LCP observer new PerformanceObserver((entryList) => { const entries = entryList.getEntries() const lastEntry = entries[entries.length - 1] console.log('LCP:', lastEntry.startTime) }).observe({ type: 'largest-contentful-paint', buffered: true }) // FID observer new PerformanceObserver((entryList) => { entryList.getEntries().forEach((entry) => { console.log('FID:', entry.processingStart - entry.startTime) }) }).observe({ type: 'first-input', buffered: true }) // CLS observer let clsValue = 0 new PerformanceObserver((entryList) => { entryList.getEntries().forEach((entry: any) => { if (!entry.hadRecentInput) { clsValue += entry.value console.log('CLS:', clsValue) } }) }).observe({ type: 'layout-shift', buffered: true }) }

📊 Gerçek Proje Örneği ve Sonuçlar

Bir e-ticaret sitesinde uyguladığımız optimizasyonların sonuçlarını paylaşıyoruz. Bu case study, hangi optimizasyonların en etkili olduğunu gösteriyor.

🎯 Optimizasyon Sonuçları

Lighthouse Performance

45 → 98

First Contentful Paint

3.2s → 1.1s

Largest Contentful Paint

4.8s → 2.1s

Cumulative Layout Shift

0.25 → 0.05

Optimizasyon Adımları ve Etkileri

🖼️ Image Optimization

Etki: %40 performans artışı

WebP formatı, responsive images ve lazy loading

📦 Code Splitting

Etki: %25 performans artışı

Dynamic imports ve component lazy loading

🗄️ Caching Strategy

Etki: %35 performans artışı

ISR, Redis cache ve CDN optimizasyonu

🎨 CSS Optimization

Etki: %15 performans artışı

Critical CSS ve font loading optimization

🎯 Sonuç ve Best Practices

Next.js ile Lighthouse skoru 100 almak sistematik bir yaklaşım gerektirir. Bu rehberde öğrendiklerinizi uygulayarak projelerinizde dramatik performans iyileştirmeleri elde edebilirsiniz.

💡 Önemli Hatırlatmalar

  • Performans optimizasyonu sürekli bir süreçtir
  • Her değişiklikten sonra Lighthouse testleri yapın
  • Gerçek kullanıcı verilerini de takip edin
  • Core Web Vitals metriklerine odaklanın
  • Mobile-first yaklaşımı benimseyin

🚀 Sonraki Adımlar

Core Web Vitals Next.js Docs