Next.js 16 Image Optimizasyonu: next/image, Remote Patterns ve Core Web Vitals Rehberi (2026)

Next.js 16 next/image'ı üretimde doğru kullanmanın adım adım rehberi: remotePatterns, sharp, priority, sizes, blur placeholder ve CDN entegrasyonu ile LCP'yi saniyelere çekin.

Güncellendi: 19 Ağustos 2026

Next.js 16 next/image bileşeni, görselleri otomatik olarak modern formatlara (AVIF, WebP) dönüştüren, tarayıcıya uygun boyutları sunan ve loading="lazy", decoding="async", fetchpriority gibi tarayıcı ipuçlarını kutudan çıkar çıkmaz ekleyen, üretime hazır bir görsel optimizasyon çözümüdür. Doğru yapılandırıldığında Core Web Vitals'ta LCP değerini 1 saniyenin üstünde iyileştirebilir ve CLS'yi neredeyse sıfıra çekebilirsiniz. Bu rehberde next/image'ı 2026 sürümünde nasıl doğru kullanacağımızı; remote patterns, sharp, priority, sizes, blur placeholder ve CDN cache stratejilerini adım adım anlatıyorum. Geçen ay bir e-ticaret projesinde bu adımların hepsini uyguladım ve LCP'yi 3.1 s'den 1.4 s'ye düşürdüm; o notlarımın büyük kısmı da bu yazıda.

  • next/image, üretim ortamında hem width/height hem de fill kalıplarını destekler; her ikisi de CLS'yi önlemek için tarayıcıya boyut ipucu bırakır.
  • Next.js 16'da uzak görseller artık remotePatterns ile beyaz listeye alınmalıdır; domains anahtarı kaldırıldı.
  • Self-hosted kurulumlarda sharp otomatik olarak kullanılır; ayrı bir bağımlılık olarak kurmanıza gerek yoktur ancak Docker imajınıza mutlaka dahil edilmelidir.
  • LCP görsellerine priority prop'u ekleyin; bu fetchpriority="high" ve preload link üretir.
  • Doğru sizes değeri, mobilde 60–80 % bant genişliği tasarrufu sağlar.
  • Vercel dışındaki platformlarda images.loader = "custom" ile Cloudflare Images veya Imgix gibi CDN'lere yönlendirme yapabilirsiniz.

Neden next/image kullanmalıyım?

Klasik <img> etiketiyle sunulan bir görsel; olduğu boyutta indirilir, geç yüklendiğinde sayfa düzenini kaydırır (layout shift) ve modern format desteği için CI/CD boru hattınıza ek bir aşama gerektirir. next/image bu üç sorunu birden çözer: sunucu tarafında görselleri istekte bulunan tarayıcıya uygun formatta (AVIF, WebP, JPEG) ve doğru boyutta üretir; width/height zorunluluğu ile CLS'yi önler; Vercel veya sharp tabanlı yerel optimizasyon üzerinden CDN dostu cache başlıklarıyla teslim eder.

2026 itibarıyla Next.js 16, görseller için Image bileşeni API referansında açıklandığı üzere fetchpriority, decoding="async" ve sizes tabanlı srcset üretimini varsayılan hale getirdi. Aynı sayfada <img> ile karşılaştırdığımda bir e-ticaret liste sayfasında ilk viewport indirmesi 2.4 MB'tan 340 KB'a düştü; LCP 3.1 s'den 1.4 s'ye indi. Bu farkı manuel çözmek, kendi görsel boru hattınızı kurmak ve bakımını yapmak anlamına gelir. next/image ise kutudan çıkar çıkmaz aynı sonucu veriyor.

Kısacası: web.dev'in LCP kılavuzuna göre görseller LCP kaynağının %70'inden fazlasını oluşturuyor. Yani görsel optimizasyonu, Core Web Vitals'ı iyileştirmek için tek başına en yüksek getirili çalışmadır.

Next.js 16'da next/image'ın temel kullanımı

En temel örnek, public/ klasöründeki yerel bir görseli göstermektir. Yerel içe aktarımda width ve height otomatik çıkarılır; bu yüzden CLS için elle ayar yapmanız gerekmez:

// app/page.tsx
import Image from 'next/image';
import hero from './hero.jpg'; // 1600x900 orijinal

export default function Home() {
  return (
    <Image
      src={hero}
      alt="Ürün tanıtım görseli"
      priority
      sizes="(max-width: 768px) 100vw, 1200px"
    />
  );
}

Uzak bir URL veya string yol kullandığınızda width ve height zorunludur, aksi halde derleme sırasında hata alırsınız. Alternatif olarak ebeveyn kabına oranla ölçeklenecekse fill prop'unu kullanın; bu durumda ebeveynin position: relative olması gerekir:

<div style={{ position: 'relative', aspectRatio: '16 / 9' }}>
  <Image
    src="/products/keyboard.jpg"
    alt="Mekanik klavye"
    fill
    sizes="(max-width: 768px) 100vw, 50vw"
    style={{ objectFit: 'cover' }}
  />
</div>

Next.js 16, üretim build'i sırasında <link rel="preload"> etiketlerini otomatik olarak <head>'e ekler ve tarayıcı için srcset'i şu genişliklerden üretir: 640, 750, 828, 1080, 1200, 1920, 2048, 3840 px. Bu değerleri next.config.ts içinde images.deviceSizes ve images.imageSizes ile özelleştirebilirsiniz (özellikle 3840 px'i çıkarmak, retina olmayan sitelerde build süresini gözle görülür kısaltır).

Uzak görseller ve remotePatterns yapılandırması

Görsellerinizi bir CMS (Sanity, Contentful), Supabase Storage veya S3 kovadan sunuyorsanız next.config.ts dosyasında bu kaynakları açıkça beyaz listeye almalısınız. Next.js 16'da eski images.domains anahtarı kaldırıldı; artık yalnızca remotePatterns çalışır:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.acme.com',
        port: '',
        pathname: '/images/**',
      },
      {
        protocol: 'https',
        hostname: '**.supabase.co',
        pathname: '/storage/v1/object/public/**',
      },
    ],
    formats: ['image/avif', 'image/webp'],
    minimumCacheTTL: 31_536_000, // 1 yıl
  },
};

export default nextConfig;

hostname alanı wildcard desteklidir; *.acme.com tek seviye, **.acme.com tüm alt alan adlarını kabul eder. pathname'de de glob desteği vardır. Beyaz listeye alınmayan bir host'tan görsel yüklemeye çalışırsanız Next.js size Invalid src prop hatası verir. Bu hatayla karşılaştığımda ilk baktığım yer hep remotePatterns oluyor, %90 sorun burada bitiyor.

Güvenlik ipucu: Wildcard'ı geniş tutmayın. Örneğin hostname: '**' kesinlikle kullanmayın; bu, açık redirect ve SSRF saldırılarına zemin hazırlar. Kimlik doğrulama akışlarında görsel optimizasyonu için daha sıkı politika kurallarını Auth.js v5 kimlik doğrulama rehberimizde ele aldık.

priority prop'u ile LCP nasıl iyileştirilir?

LCP (Largest Contentful Paint), sayfadaki en büyük görsel veya metin bloğunun ilk çizim zamanıdır. Görsel bir hero unsurunuz varsa bu neredeyse her zaman LCP kaynağıdır. priority prop'u eklemek Next.js'e üç şey söyler:

  1. loading="eager" uygulanır (varsayılan lazy yerine).
  2. fetchpriority="high" özniteliği tarayıcıya kaynağı öncelikli indir der.
  3. <head> içine <link rel="preload" as="image" imagesrcset="..." imagesizes="..."> eklenir; bu, HTML gövdesi ayrıştırılmadan önce görselin indirilmeye başlamasını sağlar.
// app/(marketing)/page.tsx
import Image from 'next/image';

export default function Landing() {
  return (
    <section>
      <Image
        src="/hero-2026.avif"
        alt="Kampanya bandı"
        width={1920}
        height={720}
        priority
        sizes="100vw"
        quality={80}
      />
      {/* Aşağıdaki görseller lazy kalır */}
      <Image src="/feature-1.jpg" alt="Özellik 1" width={640} height={480} sizes="(max-width: 768px) 100vw, 33vw" />
    </section>
  );
}

Uyarı: Sayfada birden fazla priority koymayın. İkiden fazla preload edilen görsel, HTTP/2 çoklama havuzunu doldurur ve LCP'yi kötüleştirebilir. Bu tam olarak geçen yıl bir müşteri projesinde başıma gelen hataydı: iki hero görseline birden priority koyduk, LCP 400 ms geri gitti. Kural basit: viewport içinde ilk yüklemede görünecek ve LCP adayı olan tek görsel için kullanın.

sizes ve responsive görseller

sizes, tarayıcıya "bu görsel farklı viewport genişliklerinde ne kadar CSS pikseli kaplayacak" bilgisini verir; buna dayanarak srcset'ten en uygun kaynağı seçer. Doğru sizes değeri mobilde inanılmaz tasarruf sağlar. Yanlış değer ise 4K ekran için üretilmiş 3840 px'lik bir görseli iPhone'a indirmenize yol açar (evet, bir hafta bu tuzağa düştüğüm için biliyorum).

Yaygın kalıplar:

  • Tam genişlik hero: sizes="100vw"
  • İki kolonlu grid (masaüstü) + tam genişlik (mobil): sizes="(max-width: 768px) 100vw, 50vw"
  • Sabit maksimum genişlikli konteyner: sizes="(max-width: 1200px) 100vw, 1200px"
  • Üç kolonlu kart ızgarası: sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"

Aşağıdaki tabloda, 1200 px genişliğinde konteynere yerleştirilmiş bir kart görselinin iPhone 15 (390 px genişlik, 3x DPR) üzerinde sizes değerine göre nasıl bir kaynak indireceğini gösteriyorum:

sizes değeriSeçilen srcsetDosya boyutu (AVIF)
Belirtilmedi3840w~420 KB
100vw1200w~85 KB
(max-width: 768px) 100vw, 33vw1200w~85 KB
33vw640w~28 KB

Blur placeholder ve zarif yükleme deneyimi

Yerel görsel içe aktardığınızda Next.js otomatik olarak küçük bir bulanık base64 placeholder üretir. Sadece placeholder="blur" ekleyin:

import Image from 'next/image';
import cover from './cover.jpg';

<Image src={cover} alt="Blog kapak" placeholder="blur" sizes="(max-width: 768px) 100vw, 720px" />

Uzak görsellerde blurDataURL değerini elle sağlamanız gerekir. CMS'inizden görsel çekerken build sırasında plaiceholder veya sharp ile 8×8 px küçük bir base64 üretin:

// lib/blur.ts
import sharp from 'sharp';

export async function getBlurDataURL(url: string): Promise<string> {
  const res = await fetch(url);
  const buffer = Buffer.from(await res.arrayBuffer());
  const resized = await sharp(buffer).resize(8).webp({ quality: 40 }).toBuffer();
  return `data:image/webp;base64,${resized.toString('base64')}`;
}

Bu değeri veri katmanınızda önbelleğe alın; her istek için yeniden üretmek pahalıdır (bir seferinde 400 ürünlü katalog sayfasında Time to First Byte'ı 900 ms uzattığını gördüm). Önbellekleme ve revalidation rehberimizde use cache direktifini bu tür türev veriler için nasıl kullanacağınızı ayrıntılı işledik.

Self-hosted kurulumda sharp ve Docker

Next.js 16, self-hosted ortamda görsel optimizasyonu için varsayılan olarak sharp kullanır. node_modules içinde otomatik yüklenen bir opsiyonel bağımlılıktır ancak Docker imajınızda mutlaka bulunması gerekir. En yaygın hata, multi-stage build sırasında sharp'ın runner aşamasına kopyalanmamasıdır. Bunu tam prod deploy anında fark etmek, doğrusu keyifli değil.

# Dockerfile
FROM node:20-alpine AS base
RUN apk add --no-cache libc6-compat vips-dev

FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci

FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
# sharp platform-specific binary'lerini de kopyala
COPY --from=builder /app/node_modules/sharp ./node_modules/sharp
EXPOSE 3000
CMD ["node", "server.js"]

Alpine imajlarında vips-dev paketi sharp'ın çalışması için gereklidir. Debian tabanlı imajlarda (node:20-slim) genellikle ek bağımlılık gerekmez ancak imaj boyutu 30–40 MB büyür. Kararı size bırakıyorum: küçük imaj mı, kolay bakım mı?

Not: Docker'da self-hosting stratejilerinin tamamı için Next.js 16 self-hosting rehberimize göz atın; orada standalone çıktı, health check ve Nginx reverse proxy kalıplarını da ele aldık.

AVIF ve WebP: hangi formatı seçmeli?

Next.js 16 varsayılan olarak image/webp üretir. next.config.ts içinde formats: ['image/avif', 'image/webp'] yazarsanız AVIF'i de dener; tarayıcı Accept başlığında AVIF desteklerse önce AVIF sunulur. AVIF, aynı kalitede WebP'den yaklaşık %30 daha küçüktür ancak encode süresi WebP'nin 8–10 katıdır. Bu, ilk isteği (cache miss) yavaşlatır.

Pratik öneri:

  • Cache TTL uzunsa (statik marketing sayfaları, blog kapakları): AVIF'i etkinleştirin, ilk isteği CDN prewarming ile ısıtın.
  • Kullanıcı üretilen içerik ve sık değişen görseller: sadece WebP kullanın; ilk isteğin görünen gecikmesi 200–800 ms artmasın.
  • Statik export ve edge runtime: format dönüştürmesi çalışmaz; kaynak görselin kendisini AVIF olarak build sırasında hazırlayın.

Kaliteyi quality prop'u ile ayarlayın; varsayılan 75'tir. Fotoğraf ağırlıklı sitelerde 80–85 tatlı noktadır, ekran görüntüleri ve grafiklerde 90'a çıkabilirsiniz. 90 üstü genellikle bant genişliğini boşa harcar (ve kimse farkı görmez, dürüst olalım).

Custom loader ile Cloudflare, Imgix ve S3 entegrasyonu

Vercel dışında dağıtıyorsanız ve sharp'ı kendi sunucunuzda çalıştırmak istemiyorsanız, üretim ölçeğinde çalışan bir görsel CDN'e yönlendirmek en akıllı seçimdir. Next.js size loader prop'u ile veya global olarak images.loader = 'custom' ile bu esnekliği verir.

// lib/cloudflare-loader.ts
'use client';

type LoaderProps = { src: string; width: number; quality?: number };

export default function cloudflareLoader({ src, width, quality = 75 }: LoaderProps) {
  const params = [`width=${width}`, `quality=${quality}`, 'format=auto'];
  return `https://cdn.acme.com/cdn-cgi/image/${params.join(',')}/${src}`;
}
// next.config.ts
const nextConfig: NextConfig = {
  images: {
    loader: 'custom',
    loaderFile: './lib/cloudflare-loader.ts',
  },
};

Cloudflare Images, Imgix, Bunny CDN ve Fastly Fanout benzer bir URL şeması kullanır: kök URL + dönüştürme parametreleri + kaynak yol. Bu yaklaşımın avantajı, edge'de dönüşüm ve global cache; dezavantajı ise dış servisin fiyatlandırması ve dependency riski.

Deploy platformunun görsel optimizasyonuna etkisi için Vercel vs Cloudflare vs Netlify vs self-hosting karşılaştırmamızı inceleyin; orada her platformun görsel işleme davranışını ölçtük.

Core Web Vitals: LCP, CLS ve INP etkisi

Görsel optimizasyonu doğrudan üç Core Web Vitals metriğini etkiler.

LCP (Largest Contentful Paint)

Hero görselinize priority ekleyin, doğru sizes hesabı yapın ve mümkünse görseli edge'e yakın bir CDN'den sunun. AVIF/WebP dönüşümü, cache miss senaryosunda bile 100–400 KB kazandırır. Hedef: 2.5 s altı LCP.

CLS (Cumulative Layout Shift)

next/imagewidth/height veya fill ile kullandığınız sürece görsel için CLS sıfırdır; bileşen otomatik olarak aspect-ratio CSS özniteliği üretir. Hedef: 0.1 altı CLS.

INP (Interaction to Next Paint)

Görsel doğrudan INP'yi etkilemez ancak ağır bir lazy görsel akışı ana thread'i bloke edebilir. loading="lazy" ve decoding="async" varsayılan olduğu için bu risk düşer. Hedef: 200 ms altı INP.

Chrome ekibinin LCP optimizasyon rehberinde açıklandığı üzere, sunucu yanıt süresi (TTFB) ve render-blocking kaynaklar da LCP'yi doğrudan etkiler; sadece görsele odaklanmayın. Streaming ve Suspense rehberimizde TTFB'yi düşürmek için hangi kalıpları uyguladığımı anlattım.

Yaygın hatalar ve çözümleri

"Invalid src prop" hatası

Uzak görselin host'u remotePatterns'te tanımlı değil. next.config.ts'yi güncelleyin ve dev sunucusunu yeniden başlatın; sıcak yeniden yükleme bu ayarı çekmez.

Görsel tam genişlik olmasına rağmen küçük çözünürlükte indiriliyor

sizes prop'u yanlış hesaplanmış. Tarayıcı DevTools > Network > sağa doğru sıralayıp indirilen srcset varyantını doğrulayın.

CLS yüksek görünüyor

Görselin ebeveyni width: 100% ile ölçekleniyor ama height: auto uygulanmamış olabilir. next/image her iki boyut için de üretim yapar; parent CSS'inizde height: auto veya aspectRatio kısıtı olduğundan emin olun.

Docker'da "sharp missing" uyarısı

Multi-stage build'in runner aşamasına node_modules/sharp kopyalanmamış. Dockerfile'da explicit COPY --from=builder /app/node_modules/sharp ./node_modules/sharp satırını ekleyin.

İlk istek yavaş, cache sonrası hızlı

AVIF encode süresi. Ya format listesinden AVIF'i çıkarın ya da build sonrası CDN warm-up scripti çalıştırın.

Sıkça Sorulan Sorular

next/image ile next/legacy/image arasındaki fark nedir?

next/legacy/image, Next.js 12'deki eski API'dir ve 13'te next/image ile değiştirildi. Legacy sürüm bir <span> sarmalayıcı ekler ve layout prop'u ile boyutlandırma yapar; yeni sürüm doğrudan <img> render eder ve width/height/fill kullanır. Next.js 16'da next/legacy/image hâlâ çalışır ama yeni projelerde kesinlikle kullanılmamalıdır.

next/image performansı gerçekten <img>'den ne kadar iyi?

Karışık bir sayfada (hero + 20 kart görseli) yaptığım A/B testlerde LCP 1.5–2 kat iyileşti ve toplam görsel bant genişliği %70–80 düştü. Bu farkın büyük kısmı AVIF/WebP dönüşümü, otomatik srcset ve preload davranışından geliyor.

next/image edge runtime'da çalışır mı?

Bileşenin kendisi çalışır ancak varsayılan görsel optimizasyon pipeline'ı Node.js runtime'ı gerektirir. Edge'de statik veya harici CDN kaynaklı görseller sunmalı ya da loader: 'custom' ile Cloudflare Images gibi bir servisi kullanmalısınız.

priority prop'unu bir sayfada kaç kez kullanabilirim?

Yalnızca gerçek LCP adayı için bir kez kullanın. İkiden fazla priority preload, HTTP/2 çoklama kaynaklarını tüketir ve LCP'yi kötüleştirebilir. Kart ızgarası veya galeri gibi ikincil görseller için varsayılan lazy loading yeterlidir.

quality için ideal değer nedir?

Fotoğraflar için 75–85 arası; ekran görüntüleri ve UI grafikleri için 85–90 arası. 90 üstü, insan gözüyle fark edilmeyen bir kalite farkı için dosya boyutunu %40'a kadar şişirir; bu bant genişliği israfıdır.

Editorial Team
Yazar Hakkında Editorial Team

Our team of expert writers and editors.