Web Storage

localStorage dan sessionStorage untuk state yang bertahan setelah reload.

Daftar isi

Setiap kali halaman dimuat ulang, seluruh variabel JavaScript kembali kosong, padahal banyak aplikasi butuh data yang bertahan lebih lama, misalnya tema pilihan atau draf catatan yang belum terkirim. Web Storage menyediakan penyimpanan pasangan kunci-nilai di dalam browser melalui dua object yang saling melengkapi. localStorage menyimpan data yang bertahan tanpa batas waktu, sedangkan sessionStorage menampung data yang hanya hidup selama satu sesi tab. Topik ini membahas keduanya, aturan mainnya, serta cara menyimpan object lewat JSON.

localStorage dan sessionStorage#

Keduanya memakai API yang identik, setItem(), getItem(), removeItem(), dan clear(), sehingga yang membedakan hanya masa simpannya. Data pada localStorage tetap ada meskipun tab ditutup dan komputer dimatikan, sedangkan data pada sessionStorage hilang begitu tabnya ditutup. Data juga tidak saling bocor antarsitus, karena penyimpanan dipisahkan per origin, yaitu kombinasi protokol, domain, dan port halaman yang sedang berjalan.

AspeklocalStoragesessionStorage
Masa simpanbertahan sampai dihapushilang saat tab ditutup
Jangkauan bacasemua tab satu origintab yang sama saja
Contoh pemakaiantema, preferensi tampilandraf form pada satu tab

Pemilihan antar keduanya mengikuti umur data yang disimpan. Preferensi yang sebaiknya diingat di kunjungan berikutnya masuk localStorage, sedangkan kerja sementara yang hanya relevan pada satu tab masuk sessionStorage. Kapasitas keduanya terbatas, umumnya sekitar lima megabita per origin, sehingga cukup untuk teks dan pengaturan tetapi bukan untuk berkas gambar.

Perubahan pada localStorage juga bisa dipantau lintas tab lewat event storage. Tab yang menyimpan data memicu event tersebut pada tab lain satu origin, dan handlernya dapat membaca event.key beserta nilai barunya. Pola ini berguna untuk hal-hal seperti menyamakan tema pada banyak tab yang terbuka, karena tab yang lain ikut tahu ketika preferensinya berganti.

Kunci, nilai, dan JSON#

Seluruh nilai yang disimpan diperlakukan sebagai string. Angka dan boolean otomatis diubah menjadi teks saat disimpan, sedangkan object dan array berubah menjadi tulisan "[object Object]" yang tidak berguna. Untuk data berstruktur, nilai dikemas dengan JSON.stringify() sebelum disimpan dan dibongkar dengan JSON.parse() setelah dibaca.

js Contoh: menyimpan dan membaca object lewat JSON
// tidak-dijalankan
const preferensi = { tema: "gelap", ukuranHuruf: 14 };

localStorage.setItem("preferensi", JSON.stringify(preferensi));

const terbaca = JSON.parse(localStorage.getItem("preferensi"));
console.log(terbaca.ukuranHuruf); // 14

Kuncinya juga bebas diberi nama, dan konvensi yang lazim memakai awalan aplikasi seperti "catatan-draf" agar tidak bertabrakan dengan kunci aplikasi lain pada origin yang sama. Melepas satu kunci memakai removeItem(), sedangkan clear() mengosongkan seluruh penyimpanan origin tersebut, jadi metode kedua perlu dipakai dengan hati-hati pada aplikasi yang berbagi origin.

Membaca dengan aman saat halaman dibuka#

getItem() mengembalikan null bila kuncinya belum pernah disimpan, dan nilai null ini menjadi tanda alami untuk memuat nilai bawaan. Pemeriksaannya dilakukan setiap halaman dibuka, sebab halaman tidak pernah tahu apakah kunjungan sebelumnya sempat menyimpan sesuatu. Selain itu, lingkungan seperti mode privat dapat memblokir penyimpanan, sehingga aksesnya dibungkus try/catch agar halaman tetap berjalan.

Ketersediaan penyimpanan juga dapat dideteksi lebih dahulu alih-alih menunggu error muncul. Pola yang lazim menguji localStorage dengan mencoba menulis dan membaca satu kunci percobaan di dalam try/catch, lalu menandai aplikasi untuk beralih ke mode tanpa penyimpanan bila ujiannya gagal. Dengan tanda itu, tombol simpan dapat disembunyikan dan pesan penggantinya ditampilkan sejak awal, tanpa kejutan di tengah pemakaian.

js Contoh: memuat draf dengan nilai bawaan
// tidak-dijalankan
let draf;

try {
  draf = localStorage.getItem("catatan-draf");
} catch (error) {
  draf = null; // penyimpanan tidak tersedia di lingkungan ini
}

if (draf !== null) {
  console.log("Draf dimuat, panjang " + draf.length + " karakter");
} else {
  console.log("Belum ada draf tersimpan");
}

Demo berikut menyimpan satu textarea catatan ke localStorage ketika tombol Simpan ditekan, lalu memuatnya kembali setiap halaman dibuka. Tombol Hapus melepas kuncinya dengan removeItem(). Tekan Simpan, muat ulang halaman, dan catatannya akan tampil kembali beserta panjang karakternya. Bila lingkungan yang dipakai memblokir penyimpanan, panel keterangan menampilkan pesan penggantinya alih-alih membiarkan halaman error. Sebagai latihan lanjutan, ganti nama object penyimpanannya ke sessionStorage dan rasakan draf yang hilang bersama tabnya.

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <title>Demo localStorage untuk draf catatan</title>
  <style>
    body { font-family: "Segoe UI", Arial, sans-serif; margin: 14px; font-size: 14px; color: #1f2937; }
    label { display: block; font-weight: 600; margin-bottom: 6px; }
    textarea { width: 340px; max-width: 100%; height: 90px; padding: 8px; font: 14px "Segoe UI", Arial, sans-serif; }
    button { padding: 6px 12px; margin: 10px 8px 0 0; cursor: pointer; }
    #info { margin-top: 12px; padding: 10px; background: #eef2ff; border: 1px solid #4338ca; border-radius: 8px; min-height: 20px; max-width: 380px; }
  </style>
</head>
<body>
  <label for="catatan">Catatan</label>
  <textarea id="catatan" placeholder="Tulis catatan di sini"></textarea>
  <br>
  <button id="simpan" type="button">Simpan catatan</button>
  <button id="hapus" type="button">Hapus catatan</button>
  <div id="info"></div>
  <script src="index.js"></script>

<script>
// Demo Web Storage: draf catatan tersimpan di localStorage dan dimuat kembali.

const area = document.querySelector("#catatan");
const info = document.querySelector("#info");
const KUNCI = "catatan-demo";

function muat() {
  try {
    const tersimpan = localStorage.getItem(KUNCI);
    if (tersimpan !== null) {
      area.value = tersimpan;
      info.textContent = "Catatan dimuat dari localStorage, panjang " + tersimpan.length + " karakter.";
    } else {
      info.textContent = "Belum ada catatan tersimpan.";
    }
  } catch (error) {
    info.textContent = "Penyimpanan tidak tersedia di lingkungan ini.";
  }
}

document.querySelector("#simpan").addEventListener("click", () => {
  try {
    localStorage.setItem(KUNCI, area.value);
    info.textContent = "Catatan tersimpan, panjang " + area.value.length + " karakter.";
  } catch (error) {
    info.textContent = "Penyimpanan tidak tersedia di lingkungan ini.";
  }
});

document.querySelector("#hapus").addEventListener("click", () => {
  try {
    localStorage.removeItem(KUNCI);
    info.textContent = "Kunci \"" + KUNCI + "\" dihapus dari localStorage.";
  } catch (error) {
    info.textContent = "Penyimpanan tidak tersedia di lingkungan ini.";
  }
});

muat();

</script>
<script>
// Demo Web Storage: draf catatan tersimpan di localStorage dan dimuat kembali.

const area = document.querySelector("#catatan");
const info = document.querySelector("#info");
const KUNCI = "catatan-demo";

function muat() {
  try {
    const tersimpan = localStorage.getItem(KUNCI);
    if (tersimpan !== null) {
      area.value = tersimpan;
      info.textContent = "Catatan dimuat dari localStorage, panjang " + tersimpan.length + " karakter.";
    } else {
      info.textContent = "Belum ada catatan tersimpan.";
    }
  } catch (error) {
    info.textContent = "Penyimpanan tidak tersedia di lingkungan ini.";
  }
}

document.querySelector("#simpan").addEventListener("click", () => {
  try {
    localStorage.setItem(KUNCI, area.value);
    info.textContent = "Catatan tersimpan, panjang " + area.value.length + " karakter.";
  } catch (error) {
    info.textContent = "Penyimpanan tidak tersedia di lingkungan ini.";
  }
});

document.querySelector("#hapus").addEventListener("click", () => {
  try {
    localStorage.removeItem(KUNCI);
    info.textContent = "Kunci \"" + KUNCI + "\" dihapus dari localStorage.";
  } catch (error) {
    info.textContent = "Penyimpanan tidak tersedia di lingkungan ini.";
  }
});

muat();

</script></body>
</html>

Kesalahan umum#

  1. Menyimpan object tanpa JSON.stringify. Object yang dipasang langsung menjadi string "[object Object]", dan isinya tidak dapat dikembalikan. Bungkus nilainya dengan JSON.stringify() sebelum setItem(), lalu bongkar dengan JSON.parse() setiap kali dibaca.
  2. Mengira nilai yang dibaca tetap bertipe aslinya. Semua yang keluar dari getItem() adalah string, sehingga angka 14 kembali sebagai "14" dan perbandingan ketat dengan angka gagal. Konversikan atau parsing kembali nilainya sebelum dipakai.
  3. Bersandar pada Web Storage untuk data besar. Kapasitasnya terbatas sekitar lima megabita dan seluruh isinya berupa string, sehingga arsip yang besar dan berstruktur lebih cocok dititipkan ke IndexedDB pada topik berikutnya.

Ringkasan#

  • Web Storage menyimpan pasangan kunci-nilai berupa string, dengan localStorage yang bertahan tanpa batas waktu dan sessionStorage yang hilang bersama tabnya.
  • Keduanya dipisah per origin dan memakai API yang sama, yaitu setItem, getItem, removeItem, dan clear.
  • Object dan array dikemas dengan JSON.stringify() sebelum disimpan serta dibongkar dengan JSON.parse() setelah dibaca.
  • getItem() mengembalikan null untuk kunci yang belum ada, sehingga pemuatan awal selalu disertai pemeriksaan null dan pengamanan try/catch.

Referensi#