Güncelleme notu: Bu yazı Next.js 15 için yazıldı. Next.js 16'da
dynamicIOveuseCachebayrakları tek bircacheComponentsayarında birleşti;cacheLifevecacheTagde artıkunstable_öneki olmadannext/cache'ten import ediliyor. Ayrıca Next.js 15'ten itibarenfetch()istekleri varsayılan olarak önbelleğe alınmıyor; önbellek istiyorsanızcache: 'force-cache'yazmanız gerekiyor.

Cache Nedir? Neden Önemlidir?
Cache (önbellek), sık kullanılan verilerin ya da içeriklerin geçici olarak saklanmasıdır. Sunucunun aynı veriyi tekrar tekrar üretmesini engellemek, uygulamanın yanıt süresini azaltmak ve kullanıcıya daha hızlı ve tutarlı bir deneyim sunmak gibi amaçları vardır.
Yazıyı hazırlarken Next.js'in resmi dökümantasyonuna sık sık başvurdum. Karmaşık görünen kavramları daha anlaşılır hale getirebilmek için olabildiğince örneklerle ilerlemeye çalıştım. Şimdi birlikte bu konulara biraz daha yakından bakalım.
Bir baristayı düşünün. Her sabah aynı müşteri geliyor ve "latte, orta boy, az şekerli" diyor. Barista, her seferinde "hangi boy, kaç şeker?" diye sormak yerine artık bu siparişi hatırlıyor ve müşteri gelir gelmez içeceği hazırlıyor.
Bu hafıza, cache'tir. Aynı giriş → saklanmış, hızlı bir yanıt. Eğer müşteri siparişini değiştirirse (örneğin vanilyalı isterse), cache güncellenir.
Next.js'te Caching
Next.js 15, App Router mimarisiyle birlikte, içeriklerin otomatik olarak önbelleğe alınmasını sağlayan gelişmiş bir caching sistemi sunar. Amaç; minimum veri tekrarı, düşük sunucu maliyeti ve yüksek performanstır.
Page-Level Caching (Sayfa Düzeyinde Önbellekleme)
page.tsx, layout.tsx gibi App Router bileşenleri, varsayılan olarak statik şekilde render edilir ve CDN üzerinden cache'lenir.
Bu davranış dynamic ve revalidate ayarlarıyla özelleştirilebilir:
export const dynamic = 'force-static'; // Varsayılan
// veya
export const revalidate = 300; // Incremental Static Regeneration (ISR) benzeri
// veya
export const dynamic = 'force-dynamic'; // Her istekte yeniden render edilir
Data-Level Caching with fetch() (Veri Düzeyinde Önbellekleme)
Next.js'te fetch() fonksiyonu cache-aware çalışır. İstenirse farklı cache stratejileri uygulanabilir:
force-cache:
await fetch('https://api.example.com/products', { cache: 'force-cache' });
- İlk istek sunucuya gider, içerik cache'e alınır. Sonraki isteklerde cache'ten döner.
revalidate:
await fetch('https://api.example.com/products', {
next: { revalidate: 60 },
});
- Veri ilk istekle birlikte cache edilir. 60 saniye sonra arka planda yenilenir. "Stale-while-revalidate" stratejisine uygundur.
no-store:
await fetch('https://api.example.com/user/me', {
cache: 'no-store', // SSR ile eşdeğer davranır
});
- Her istek doğrudan sunucuya gider. Cache tamamen devre dışıdır.
Fonksiyon Düzeyinde Önbellekleme (Function-level Caching)
cache() fonksiyonu ve use cache:
Next.js, React 19 ile birlikte gelen cache() fonksiyonunu kullanarak fonksiyon düzeyinde caching imkanı sunar. Bu yöntemle, IO ağırlıklı fonksiyonlar önbelleğe alınabilir.
Aynı parametrelerle çağrılan async fonksiyonlar, tekrar çalıştırılmak yerine cache'ten sonuç döner.
import { cache } from 'react';
const getProduct = cache(async (id: string) => {
const res = await fetch(`https://api.example.com/products/${id}`);
return res.json();
});
getProduct("123")ilk çağrıldığında fetch yapılır. Aynı parametreyle tekrar çağrıldığında cache'ten gelir. Bu işlem yalnızca server-side çalışır (React Server Component).
Dikkat edilmesi gerekenler:
cache()sadece Server Component'lerde veya Server Action'larda kullanılabilir.- Parametrelerin değişmemesi gerekir; aksi takdirde yeniden çalışır.
- Memory-based çalışır; dağıtık ortamda shared cache değildir.
Route Handlers ile Caching
Next.js App Router'da, route handler'lar (app/api/**/route.ts) varsayılan olarak cache: 'no-store' davranışıyla çalışır, önbelleğe alınmaz. Bu, her gelen isteğin, işleyicinin kodunu tekrar çalıştırması anlamına gelir.
Örnek: /api/time gibi zamanı döndüren bir endpoint tanımladığınızda, her sayfa yenilemede yeni bir zaman değeri döner. Çünkü bu endpoint'te önbelleğe alma devrede değildir ve bu genellikle istenen davranıştır.
// app/api/time/route.ts
export async function GET() {
return Response.json({
time: new Date().toISOString(),
});
}
Next.js, sadece GET istekleri için önbelleğe alma imkanı sunar. POST, PUT, DELETE gibi metotlar hiçbir zaman önbelleğe alınmaz. GET metodu kullanılsa bile, aşağıdaki durumlar varsa önbelleğe alma devre dışı olur:
// Örnek olarak request.headers, cookies gibi yapılar
const token = request.headers.get("Authorization");
request.headers,request.cookies,next/headersveyanext/cookiesgibi yardımcı fonksiyonlar route handler'ı otomatik olarak "dynamic" olarak işaretler, bu da her istekte yeniden render anlamına gelir. Bu nedenle önbellek stratejisi oluştururken verinin dinamikliği dikkatle değerlendirilmelidir.
export const dynamic = 'force-static'
Eğer route handler'daki veriler nadiren değişiyorsa (örneğin kategori listesi gibi), bu durumda tam önbelleğe alma tercih edilebilir.
export const dynamic = 'force-static';
export async function GET() {
return Response.json({
time: new Date().toISOString(),
});
}
- Bu yapılandırma sayesinde, route handler ilk çalıştırıldığında çıkan sonuç önbelleğe alınır.
- Sonraki tüm istekler, önbellekten hızlıca döner.
- Bu, özellikle veritabanı sorgularını azaltmak ve performansı artırmak için oldukça etkilidir.
export const revalidate = <saniye>
export const revalidate = 60; // Incremental Static Regeneration (ISR) benzeri çalışır.
export async function GET() {
const data = await fetchData();
return Response.json(data);
}
💡 Not: Geliştirme ortamında (npm run dev) önbelleğe alma aktif değildir. Gerçek etkisini görmek için üretim ortamında (npm run build && npm run start) test edilmelidir.
DynamicIO ile Otomatik Cache Davranışı
Next.js 15, bir bileşen veya route içeriği herhangi bir async işlem (ör. fetch(), db sorgusu, readFile()) içeriyorsa, bu içeriği otomatik olarak dinamik olarak değerlendirir. Bunu dynamicIO: true ile de etkinleştirebiliriz.
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
},
}
export default nextConfig
Varsayılan olarak verilerin çalışma zamanında çekilmesini sağlarken, uygulamanızın belirli bölümlerini sayfa, fonksiyon veya component düzeyinde use cache ile önbelleğe alarak cache davranışını özelleştirebilirsiniz.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
useCache: true,
},
}
export default nextConfig
// Alternatif olarak dynamicIO: true/false ile davranışı kontrol edebiliriz.
Aslında 'use client' ve 'use server' direktifleri bileşenin nerede çalışacağını tanımlıyorsa, 'use cache' de yalnızca bir string literal veya JavaScript direktifi kullanarak verinin cache'den mi yoksa run time'dan mı alınacağını belirlemeni sağlar.
'use cache' bir fonksiyona, component'a veya hatta tüm bir dosyaya uygulanabilir.
// File level
'use cache'
export default async function Page() {
// ...
}
// Component level
export async function MyComponent() {
'use cache'
return <></>
}
// Function level
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}
Nasıl Çalışır?
- Cache Key (Önbellek Anahtarı): Next.js App Router'da, Server Components ve Server Actions gibi önbelleklenebilir fonksiyonların cache anahtarı şu bileşenlerden oluşur:
Cache Key = Build ID + Function Signature + Serializable Arguments
Deterministik önbellekleme: Eğer aynı serileştirilebilir parametrelerle bir fetch(), server action veya async component tekrar çağrılırsa, fonksiyon tekrar çalışmaz; Next.js mevcut cache'ten yanıt döner (SSG veya ISR gibi davranır).
Serileştirilemeyen argümanlar (non-serializable): JSX (<Component />), children, fonksiyonlar, class instance'ları gibi serileştirilemeyen argümanlar, cache key'e dahil edilmez.
- Ancak bu tür veriler interleaving (iç içe geçirme) sayesinde render sırasında yine de fonksiyon içinde kullanılabilir.
export default async function Layout({ children }: { children: React.ReactNode }) {
const data = await fetch("https://api.example.com", {
cache: "force-cache"
});
return (
<div>
<Sidebar />
{children} {/* children önbellek anahtarına dahil edilmez ama içeride render edilir */}
</div>
);
}
Next.js'in canary sürümünde tanıtılan
cacheLifevecacheTag, geliştiricilere Next.js uygulamalarındaki önbellekleme stratejileri üzerinde önemli kontrol sağlar.cacheLifeile önceden tanımlanmış veya özel profiller aracılığıyla önbellek ömrünü hassas bir şekilde ayarlayabilirken,cacheTagbelirli önbellek girişlerinin isteğe bağlı olarak geçersiz kılınmasını kolaylaştırarak verilerin her zaman güncel kalmasını sağlar.
cacheLife
cacheLife fonksiyonu, bir component veya async fonksiyonun önbellekte ne kadar süreyle saklanacağını, ne zaman yeniden doğrulanacağını ve ne zaman tamamen geçersiz olacağını tanımlar.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
cacheLife: {
blog: {
stale: 3600, // 1 hour
revalidate: 900, // 15 minutes
expire: 86400, // 1 day
},
},
},
}
export default nextConfig
Her profil bir stale (tarayıcıda eski verinin gösterilme süresi), revalidate (arka planda güncelleme sıklığı) ve expire (tamamen geçersizleşme süresi) tanımlar.
Daha sonra, cacheLife fonksiyonunu ilgili fonksiyon veya component içinde içe aktarabilir ve çağırabilirsiniz:
import { unstable_cacheLife as cacheLife } from 'next/cache'
export async function getCachedData() {
'use cache'
cacheLife('blog')
const data = await fetch('/api/data')
return data
}
cacheTag
Önbelleğe alınmış bir component/fonksiyonu etiketlemenizi sağlar. Böylece belirli etiketlerle işaretlenmiş veriler, isteğe bağlı olarak yeniden doğrulanabilir veya temizlenebilir.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
dynamicIO: true, // burada enable edebiliyoruz
},
}
export default nextConfig
Next.js App Router'da cacheTag fonksiyonu, bir veri kaynağına ait cache'i ilişkilendirmek için kullanılır ve parametre olarak tek bir string ya da string[] alır.
// app/data.ts
import { unstable_cacheTag as cacheTag } from 'next/cache'
export async function getProducts() {
'use cache'
cacheTag('products', 'homepage')
const res = await fetch("https://api.example.com/data")
return res.json()
}
Sonrasında şu şekilde cache'i manuel olarak invalid edebilirsin:
'use server'
import { revalidateTag } from 'next/cache'
export async function submit() {
await doSomething()
revalidateTag('products') // products tag'li cache'leri siler ve yeniden fetch etmeye zorlar (ISR gibi çalışır).
}
Sonuç
Next.js 15 ile gelen yeni önbellekleme mimarisi, büyük ölçekli projelerden bireysel uygulamalara kadar tüm kullanım senaryoları için güçlü bir temel sunmaktadır. Deneysel olmasına rağmen use cache, cacheLife ve cacheTag gibi direktifler, geliştirici deneyimini ve uygulama performansını doğrudan artırır. Bu yaklaşım, React Server Component mimarisiyle uyumlu şekilde ilerleyerek geleceğin web mimarisine sağlam bir zemin hazırlar.
Umarım bu rehber sizin için faydalı olmuştur. Keyifli kodlamalar! ☕💻
Bu yazı ilk olarak Medium'da yayınlandı.