localStorage ve sessionStorage, tarayıcının her köken (origin) için ayırdığı basit anahtar/değer depolarıdır. Veri yazmak için setItem(anahtar, deger), okumak için getItem(anahtar), silmek için removeItem() ve clear() kullanırsınız. İkisi arasındaki tek fark ömürdür: localStorage tarayıcı kapanıp açılsa da kalır, sessionStorage ise yalnızca o sekmenin oturumu sürdüğü kadar yaşar. Her iki depo da yalnızca metin (string) saklar ve işlemleri eş zamanlı (senkron) yürütür.
Bu yazıda Web Storage API'nin Storage arayüzünü yöntem yöntem inceliyoruz: hangi durumda hangi deponun seçileceğini, nesneleri neden JSON'a çevirmeniz gerektiğini, başka sekmelerdeki değişiklikleri storage olayıyla nasıl yakalayacağınızı, kota dolduğunda ve depolama kapalı olduğunda ne olduğunu ve en sık yapılan hataları anlatıyoruz. Dizinin tamamı için Web API öğrenme rehberi sayfasına bakabilirsiniz.
Web Storage API nedir, çerezden farkı ne?

Web Storage API, tarayıcıda küçük miktarda veriyi anahtar/değer çiftleri olarak tutmanın çerezlere göre çok daha sade bir yoludur. Çerezler her HTTP isteğiyle sunucuya gider; Web Storage ise veriyi yalnızca tarayıcıda tutar ve sunucuya kendiliğinden hiçbir şey göndermez. Bu yüzden tema tercihi, yarım kalmış form taslağı, son açılan sekme gibi yalnızca istemcinin bilmesi gereken bilgiler için uygun bir yerdir. Oturum kimliği gibi sunucunun her istekte görmesi gereken değerler ise çerezde kalır; bu ayrımı document.cookie ve Set-Cookie yazısında ayrıntılı ele alıyoruz.
Sayfa window.localStorage veya window.sessionStorage özelliğine eriştiğinde bir Storage nesnesi döner. Aynı köken için yerel depo ile oturum deposu ayrı nesnelerdir; birine yazdığınız değer diğerinde görünmez. Köken, şema (http veya https), alan adı ve port üçlüsüdür. MDN özellikle şunun altını çizer: http://example.com ile https://example.com aynı site gibi görünse de farklı localStorage alanlarına sahiptir. Sitenizi HTTP'den HTTPS'ye taşıdığınızda eski yerel verilerin "kaybolması" bu yüzdendir. Köken kavramının güvenlik tarafı için CORS ve aynı köken politikası yazısına göz atabilirsiniz.
| Özellik | localStorage | sessionStorage |
|---|---|---|
| Ayrıldığı birim | Yalnızca köken | Köken ve sekme (üst düzey tarama bağlamı) |
| Ömür | Süresiz, tarayıcı kapansa da kalır | Sekme veya pencere kapanınca silinir |
| Yenileme ve geri yükleme | Etkilemez | Sayfa yenilense veya geri yüklense de kalır |
| Başka sekmeler | Aynı kökendeki tüm sekmeler paylaşır | Her sekmenin kendi alanı vardır |
| Gizli pencere | Son gizli sekme kapanınca silinir | Sekme kapanınca silinir |
| Saklanan tür | Yalnızca string | Yalnızca string |
Storage arayüzünün yöntemleri
Storage arayüzü küçüktür: bir özellik ve beş yöntem. setItem(keyName, keyValue) anahtar yoksa yeni kayıt oluşturur, varsa değerini günceller ve undefined döndürür. getItem(keyName) değeri string olarak verir; anahtar yoksa null döner. removeItem(keyName) tek bir kaydı, clear() o kökenin o depodaki tüm kayıtlarını siler. length kayıt sayısını, key(index) ise sıfırdan başlayan bir sıra numarasındaki anahtarın adını verir; o sırada anahtar yoksa yine null gelir.
// Yazma ya da güncelleme
localStorage.setItem("myCat", "Tom");
// Okuma: anahtar yoksa null döner
const cat = localStorage.getItem("myCat");
// Tek kaydı silme
localStorage.removeItem("myCat");
// Bu kökenin yerel deposunu tamamen boşaltma
localStorage.clear();Tüm anahtarları dolaşmak için length ile key() yöntemini birlikte kullanırsınız. MDN'in örneği tam olarak budur: sıfırdan length değerine kadar sayan bir döngü, her adımda key(i) ile anahtar adını, ardından getItem() ile değerini alır. Sıra numarası bir kayıt kimliği değildir; yalnızca dolaşmak için bir araçtır.
for (let i = 0; i < localStorage.length; i++) {
const anahtar = localStorage.key(i);
console.log(anahtar, localStorage.getItem(anahtar));
}MDN, değerlere localStorage.colorSetting = "#a4509b" veya localStorage["colorSetting"] gibi nesne özelliği sözdizimiyle de erişilebildiğini gösterir, ama hemen ardından bunu önermez. Özellik sözdizimi, clear veya getItem gibi yerleşik yöntem adlarıyla çakışabilir; kullanıcıdan gelen bir anahtarı bu yolla yazmak prototip kirlenmesi (prototype pollution) gibi güvenlik sorunlarına kapı aralayabilir. Her zaman setItem(), getItem(), removeItem(), key() ve length kullanın.
Yalnızca string saklanır: nesneleri JSON ile kaydetme
Web Storage değerleri UTF-16 metin olarak tutar. Sayı veya mantıksal değer yazdığınızda tarayıcı bunu sessizce stringe çevirir; geri okuduğunuzda 42 sayısı yerine "42" metnini alırsınız. Nesnede durum daha kötüdür: setItem("user", person) çağrısı nesnenin toString() sonucunu, yani "[object Object]" metnini saklar ve asıl veri kaybolur. Düz nesne ve diziler için çözüm, yazmadan önce JSON.stringify(), okuduktan sonra JSON.parse() kullanmaktır. JSON'un kendisini JSON.parse ve JSON.stringify yazısında anlatıyoruz.
const person = { name: "Alex" };
localStorage.setItem("user", person);
console.log(localStorage.getItem("user")); // "[object Object]"; not useful!
localStorage.setItem("user", JSON.stringify(person));
console.log(JSON.parse(localStorage.getItem("user"))); // { name: "Alex" }Yukarıdaki çıktı yorumları MDN örneğinden alınmıştır. Tarayıcının arka planda yaptığı string dönüşümünü, aynı kuralları uygulayan String() ile kendiniz de görebilirsiniz; aşağıdaki kod tarayıcıdan bağımsız olduğu için Node.js ile çalıştırılıp çıktıları doğrulandı:
const person = { name: "Alex" };
console.log(String(person)); // [object Object]
console.log(String(42)); // 42
console.log(String(true)); // true
const metin = JSON.stringify(person);
console.log(metin); // {"name":"Alex"}
console.log(JSON.parse(metin).name); // Alex
console.log(JSON.parse(null)); // nullSon satır küçük ama kullanışlı bir ayrıntıdır: anahtar hiç yazılmamışsa getItem() null döner ve JSON.parse(null) de hata vermeden null üretir. Böylece "kayıt yoksa varsayılanı kullan" mantığını ?? varsayilan ile kurabilirsiniz. Öte yandan bozuk bir metin (örneğin elle düzenlenmiş bir değer) JSON.parse içinde hata fırlatır; bu okumayı bir try...catch içine almak iyi bir alışkanlıktır.
function ayarOku(anahtar, varsayilan) {
try {
return JSON.parse(localStorage.getItem(anahtar)) ?? varsayilan;
} catch {
return varsayilan;
}
}
function ayarYaz(anahtar, deger) {
localStorage.setItem(anahtar, JSON.stringify(deger));
}
ayarYaz("tercihler", { tema: "koyu", yaziBoyutu: 18 });
const tercihler = ayarOku("tercihler", { tema: "acik", yaziBoyutu: 16 });MDN iki sınırı daha hatırlatır. Birincisi, her veri türünü saklamanın genel bir yolu yoktur: Date, Map, Set, Blob gibi değerler JSON'a çevrilirken biçim değiştirir veya kaybolur. İkincisi, okuduğunuz nesne asıl nesnenin derin bir kopyasıdır; üzerinde yaptığınız değişiklik depoya yansımaz, tekrar setItem() çağırmanız gerekir. Yapılandırılmış veri, dosya veya büyük kayıt kümeleri için IndexedDB daha doğru bir araçtır.
sessionStorage ne zaman ve nasıl yaşar?

sessionStorage hem kökene hem sekmeye göre ayrılır. Bir belge bir sekmede açıldığında o sekmeye özgü bir sayfa oturumu oluşur; bu oturum sayfa yenilense veya tarayıcı sekmeyi geri yüklese de devam eder. Aynı sekmede aynı kökenden gelen iframe'ler bu alanı paylaşır. Sekme veya pencere kapandığında oturum biter ve veriler silinir.
MDN iki ince noktaya dikkat çeker. Sayfayı yeni bir sekmede ya da pencerede açmak yeni bir oturum başlatır; bu davranış oturum çerezlerinden farklıdır, çünkü oturum çerezleri tüm sekmelerde ortaktır. Ayrıca sayfanın bir opener değeri varsa (örneğin window.open ile açıldıysa) yeni sayfanın sessionStorage alanı başlangıçta açan sayfanınkinin bir kopyasıdır; ama sonrasında ikisi bağımsızdır ve birindeki değişiklik diğerini etkilemez. Bu kopyalamayı istemiyorsanız opener bağlantısını kaldıran yöntemlerden birini kullanmanız gerekir.
Tipik bir kullanım, kazara yenilenen sayfada metin kutusunun içeriğini korumaktır. Aşağıdaki örnek MDN'deki örnekten uyarlanmıştır:
<label for="field">Notunuz</label>
<textarea id="field"></textarea>const field = document.getElementById("field");
// Sayfa kazara yenilendiyse kaydedilmiş taslağı geri yükle
if (sessionStorage.getItem("autosave")) {
field.value = sessionStorage.getItem("autosave");
}
// Alan her değiştiğinde taslağı oturum deposuna yaz
field.addEventListener("change", () => {
sessionStorage.setItem("autosave", field.value);
});Başka sekmeleri izlemek: storage olayı
Aynı depolama alanını paylaşan başka bir belge veriyi değiştirdiğinde window üzerinde storage olayı tetiklenir. En önemli kural şudur: olay, değişikliği yapan pencerede tetiklenmez. Yani bir sekmede setItem() çağırıp aynı sekmede storage dinleyicisinin çalışmasını beklerseniz hiçbir şey olmaz. Olay, diğer sayfaların değişiklikle senkron kalması için tasarlanmıştır.
localStorage için olay aynı kökendeki diğer tüm tarama bağlamlarında, başka sekmeler dahil, tetiklenir. sessionStorage için ise yalnızca aynı sekmedeki, aynı kökenden gömülü iframe'lerde tetiklenir; başka sekmeler bu olayı almaz. Olay iptal edilemez ve kabarcıklanmaz (bubble etmez). Dinleyici eklemenin genel kuralları için addEventListener rehberine bakabilirsiniz.
Olay nesnesi bir StorageEvent örneğidir ve şu bilgileri taşır: key değişen anahtarın adıdır, değişiklik clear() ile yapıldıysa null olur. oldValue önceki değerdir; anahtar yeni eklendiyse null olur. newValue yeni değerdir; kayıt silindiyse veya depo temizlendiyse null olur. url değişikliği yapan belgenin adresini, storageArea ise etkilenen Storage nesnesini verir.
window.addEventListener("storage", (e) => {
if (e.storageArea !== localStorage) return;
if (e.key === null) {
// Başka bir sekme clear() çağırdı
temayiUygula("acik");
return;
}
if (e.key === "tema") {
// newValue null ise kayıt silinmiştir
temayiUygula(e.newValue ?? "acik");
}
});Bu kalıpla bir sekmede tema değiştirildiğinde diğer açık sekmeler de kendini günceller. Oturumu kapatma gibi daha kritik senkronizasyonlar için olaya ek olarak sunucu tarafında da kontrol yapmanız gerekir; sekmeler arası daha zengin mesajlaşma ihtiyacınız varsa postMessage ve BroadcastChannel yazısına göz atın.
Depolama kullanılabilir mi? Özellik denetimi
Tarayıcının localStorage özelliğini tanıması, onu gerçekten kullanabileceğiniz anlamına gelmez. MDN'e göre bazı tarayıcı ayarları depolamayı kapatır ama global nesneyi gizlemez; bazı tarayıcılar gizli modda kotası sıfır olan boş bir depo verir. Bu nedenle en güvenilir yol, küçük bir değer yazıp silmeyi denemektir. Aşağıdaki işlev MDN'deki örnektir:
function storageAvailable(type) {
let storage;
try {
storage = window[type];
const x = "__storage_test__";
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (e) {
return (
e instanceof DOMException &&
e.name === "QuotaExceededError" &&
// içinde zaten veri varsa QuotaExceededError depolamanın açık olduğunu gösterir
storage &&
storage.length !== 0
);
}
}
if (storageAvailable("localStorage")) {
// localStorage kullanılabilir
} else {
// bellekte tutulan bir yedek yapıya geçin
}İşlevin catch bloğundaki mantık önemlidir: QuotaExceededError alınmışsa ama depoda zaten kayıt varsa, depolama açıktır ve yalnızca doludur. Kayıt yokken aynı hatayı almak ise kotanın sıfır olduğunu, yani depolamanın pratikte kapalı olduğunu düşündürür.
localStorage ve sessionStorage özelliklerine erişmek bile hata fırlatabilir. MDN, köken geçerli bir şema/alan adı/port üçlüsü değilse (örneğin data: şeması) veya kullanıcı sitelerin veri saklamasını engelleyen bir ayar yaptıysa SecurityError türünde bir DOMException fırlatıldığını belirtir. Kullanıcı çerezleri engellediğinde tarayıcılar bunu da büyük olasılıkla veri saklama yasağı olarak yorumlar. Doğrudan diskten açılan file: sayfalarında davranış tanımsızdır ve tarayıcıdan tarayıcıya değişebilir; bu yüzden denemelerinizi yerel bir sunucu üzerinden yapın. Bu hata adlarının ayrıntılı listesi için DOMException ve güvenli bağlam hataları yazısına bakabilirsiniz.
Kota, gizli mod ve üçüncü taraf iframe'ler
MDN'in kota sayfasına göre Web Storage, köken başına en fazla 5 MiB yerel depo ve 5 MiB oturum deposu ile sınırlıdır. Sınıra ulaşıldığında setItem() QuotaExceededError fırlatır; kullanıcı ek alan vermeyi reddettiğinde de aynı hata gelir. Bu nedenle önemli yazma işlemlerini try...catch ile sarmalı ve hata durumunda eski veya gereksiz anahtarları temizleyip tekrar denemelisiniz. IndexedDB ve Cache API'nin çok daha geniş, tarayıcıya göre değişen kotaları ise depolama kotası ve kalıcı depolama yazısının konusudur.
function guvenliYaz(anahtar, deger) {
try {
localStorage.setItem(anahtar, deger);
return true;
} catch (e) {
if (e instanceof DOMException && e.name === "QuotaExceededError") {
localStorage.removeItem("eski-onbellek");
}
return false;
}
}Gizli tarama (private, incognito) pencerelerinde API'ler çalışmaya devam eder, ama MDN'e göre localStorage bu modda sessionStorage gibi davranır: gizli pencere kapandığında tüm veriler silinir. Kullanıcıların bir kısmının bu modda geleceğini varsayın ve verinin kaybolması uygulamayı bozmasın.
Üçüncü taraf kodun hangi depoya yazdığı, nasıl gömüldüğüne bağlıdır. Başka bir siteye <script> etiketiyle eklenen kod, gömen sayfanın bağlamında çalışır ve setItem() çağrıları gömen sitenin deposuna yazılır; tarayıcı açısından bu kod ile sitenin kendi kodu arasında fark yoktur. Kod bir <iframe> içinde çalışıyorsa iframe'in kökeniyle çalışır ve o kökenin deposuna yazar. Kullanıcı üçüncü taraf çerezleri kapattıysa, üçüncü taraf iframe'lerin Web Storage erişimi engellenir.
Performans: eş zamanlı API'nin bedeli
Web Storage işlemleri eş zamanlıdır: veri yazılırken, okunurken veya silinirken diğer JavaScript kodu bekler. Küçük ayarlar için bu fark edilmez; ancak MDN, büyük miktarda veriyle veya sık tekrarlanan yoğun işlemlerle kullanıldığında arayüzün takılabileceği konusunda uyarır. Her tuş vuruşunda büyük bir JSON'u yeniden yazmak yerine değişiklikleri toplayıp aralıklı yazmak, veriyi küçük parçalara bölmek veya doğrudan IndexedDB gibi eş zamansız bir API'ye geçmek daha sağlıklıdır. Ana iş parçacığını meşgul eden işlerin genel resmi için JavaScript olay döngüsü yazısına göz atabilirsiniz.
Sık yapılan hatalar

- Nesneyi doğrudan yazmak:
setItem("sepet", sepet)"[object Object]"saklar. Her zamanJSON.stringifykullanın. - Sayıyı sayı sanmak:
getItem("adet") + 1işlemi"21"gibi bir metin üretir; okuduktan sonraNumber()ile çevirin. - Aynı sekmede storage olayı beklemek: Olay değişikliği yapan pencerede tetiklenmez; kendi sekmenizde arayüzü değişikliği yaptığınız yerde güncelleyin.
- Gizli bilgileri saklamak: Aynı kökende çalışan her betik bu depoyu okuyabilir. Bir XSS açığı tüm içeriği sızdırır; oturum belirteçlerini
HttpOnlyçerezde tutmak daha güvenlidir (bkz. XSS ve CSP). - Hata yakalamamak: Kota dolduğunda veya depolama kapalıyken
setItem()hata fırlatır; yakalanmayan hata sayfanın geri kalan kodunu durdurur. - HTTP ve HTTPS'yi aynı depo sanmak: Şema kökenin parçasıdır; HTTPS'ye geçişte eski
localStorageverisi yeni kökende görünmez.
Sık Sorulan Sorular
localStorage verisi ne zaman silinir?
Bir son kullanma süresi yoktur. Siz removeItem() veya clear() çağırana, kullanıcı tarayıcı ayarlarından site verilerini silene ya da gizli pencere kapanana kadar kalır. Süreli bir kayıt istiyorsanız değerin yanına bir zaman damgası yazıp okurken kendiniz kontrol etmelisiniz.
localStorage ile sessionStorage arasında nasıl seçim yaparım?
Bilgi sekme kapandıktan sonra da hatırlanmalıysa (tema, dil tercihi) localStorage; yalnızca o sekmedeki iş akışına aitse (çok adımlı bir formun ara adımları, kazara yenilemeye karşı taslak) sessionStorage kullanın.
Web Storage'a ne kadar veri sığar?
MDN'e göre köken başına 5 MiB yerel ve 5 MiB oturum deposu vardır. Daha büyük veriler, dosyalar veya sorgulanabilir kayıtlar için IndexedDB, ağ yanıtları için Cache API tercih edilmelidir.
Service worker içinden localStorage kullanabilir miyim?
localStorage ve sessionStorage, Window arayüzünün özellikleridir; worker ve service worker bağlamlarında bulunmazlar. Bu bağlamlarda eş zamansız çalışan IndexedDB veya Cache API kullanılır; service worker temelleri için service worker yaşam döngüsü yazısına bakın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Web API öğrenme rehberi · IndexedDB kullanımı · document.cookie ve Set-Cookie · Depolama kotası · Sekmeler arası iletişim · JSON.parse ve stringify