🛞 Storage type
The Storage type is a built-in browser object used to store key–value (name–value) pairs.
This storage is simple, lightweight, and stores only strings.
You interact with it using methods like:
setItem(name, value) → store data
getItem(name) → read data
key(index) → get a key by its position
removeItem(name) → delete a specific item
clear() → delete everything
You can also check how many items are stored using:
length → number of stored entries
📌 If you store non-string data (like arrays/objects), it automatically converts them to strings.
📌 When you read them, you always get string data, so you usually use JSON.stringify() and JSON.parse().
🎟️ localStorage
localStorageis a type of Storage introduced in HTML5.It saves data with no expiration time.
This means the data stays even after closing the browser or turning off the device.
<div class="infographic-localstorage">
<style>
.infographic-localstorage {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--good: #1a8a5f;
--good-soft: #e1f2ea;
--bad: #c23b3b;
--mono-font: 'IBM Plex Mono', monospace;
--display-font: 'Fraunces', Georgia, serif;
box-sizing: border-box;
max-width: 880px;
margin: 0 auto;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
overflow-x: hidden;
}
.infographic-localstorage[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--good: #4fbf8f;
--good-soft: #16281f;
--bad: #e06b6b;
}
.infographic-localstorage * { box-sizing: border-box; }
.infographic-localstorage h1 { font-family: var(--display-font); font-weight: 600; margin: 0; }
.infographic-localstorage p { margin: 0; }
.infographic-localstorage button { font-family: inherit; }
.ls-icon { width: 1em; height: 1em; display: inline-block; vertical-align: -0.15em; }
.ls-icon svg { width: 100%; height: 100%; display: block; }
.ls-wrap { padding: clamp(1.25rem, 5vw, 2.5rem) clamp(1.1rem, 5vw, 3rem) 2rem; }
.ls-hero { text-align: center; margin-bottom: 0.6rem; }
.ls-hero h1 { font-size: clamp(1.35rem, 4.5vw, 1.85rem); }
.ls-hero p { color: var(--ink-soft); margin-top: 0.4rem; font-size: 0.86rem; }
.ls-badge-row { display: flex; justify-content: center; margin-bottom: 1.3rem; }
.ls-badge { display: inline-flex; align-items: center; gap: 0.35rem; font-family: var(--mono-font); font-size: 0.66rem;
color: var(--ink-soft); border: 1px solid var(--line); border-radius: 20px; padding: 0.3rem 0.7rem; }
.ls-badge .ls-icon { width: 12px; height: 12px; }
/* --- panels --- */
.ls-panels { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1.3rem; }
@media (max-width: 640px) { .ls-panels { grid-template-columns: 1fr; } }
.ls-panel { border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--paper); }
.ls-panel-head { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 0.75rem;
border-bottom: 1px solid var(--line); background: var(--paper-alt); }
.ls-panel-title { display: flex; align-items: center; gap: 0.4rem; font-family: var(--mono-font); font-size: 0.72rem; font-weight: 700; color: var(--ink-soft); }
.ls-panel-title .ls-icon { width: 14px; height: 14px; color: var(--accent); }
.ls-reset-btn { display: flex; align-items: center; gap: 0.25rem; background: none; border: none; color: var(--ink-soft);
font-family: var(--mono-font); font-size: 0.64rem; cursor: pointer; padding: 0.2rem 0.4rem; border-radius: 4px; }
.ls-reset-btn:hover { color: var(--accent); background: var(--accent-soft); }
.ls-reset-btn .ls-icon { width: 11px; height: 11px; }
.ls-table-head { font-family: var(--mono-font); font-size: 0.72rem; color: var(--accent); font-weight: 700; padding: 0.55rem 0.75rem 0.3rem; }
.ls-table-body { min-height: 8rem; max-height: 11rem; overflow-y: auto; padding: 0 0.5rem 0.5rem; }
.ls-row { display: flex; align-items: center; gap: 0.5rem; font-family: var(--mono-font); font-size: 0.72rem;
padding: 0.35rem 0.5rem; border-radius: 4px; animation: ls-pop-in 0.25s ease; }
.ls-row:nth-child(odd) { background: var(--paper-alt); }
.ls-row .k { color: var(--accent); font-weight: 700; }
.ls-row .sep { color: var(--ink-soft); }
.ls-row .v { color: var(--ink); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ls-row .del { background: none; border: none; color: var(--ink-soft); cursor: pointer; font-size: 0.8rem; line-height: 1; padding: 0 0.2rem; flex-shrink: 0; }
.ls-row .del:hover { color: var(--bad); }
.ls-row.is-leaving { animation: ls-pop-out 0.2s ease forwards; }
.ls-empty { font-family: var(--mono-font); font-size: 0.7rem; color: var(--ink-soft); opacity: 0.7; padding: 0.5rem; }
@keyframes ls-pop-in { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: translateY(0); } }
@keyframes ls-pop-out { to { opacity: 0; transform: translateX(6px); max-height: 0; padding: 0; margin: 0; } }
.ls-console { min-height: 8.5rem; max-height: 11.5rem; overflow-y: auto; background: #1e1e1e; padding: 0.5rem 0.7rem; }
.ls-console-line { font-family: var(--mono-font); font-size: 0.68rem; color: #d4d4d4; padding: 0.08rem 0; animation: ls-pop-in 0.2s ease; white-space: pre-wrap; }
.ls-console-line.is-echo { color: #7fb0ff; }
.ls-console-line.is-result { color: #9fd9a8; padding-left: 0.9rem; }
.ls-console-empty { font-family: var(--mono-font); font-size: 0.68rem; color: #6b6b6b; }
/* --- steps --- */
.ls-steps { display: flex; flex-direction: column; gap: 0.55rem; max-width: 560px; margin: 0 auto; }
.ls-step { display: flex; align-items: center; gap: 0.7rem; border: 1px solid var(--line); border-radius: 7px; padding: 0.5rem 0.6rem; }
.ls-step-num { font-family: var(--display-font); font-size: 1rem; color: var(--accent); width: 1.3rem; flex-shrink: 0; text-align: center; }
.ls-step-icon { width: 26px; height: 26px; border-radius: 6px; background: var(--accent-soft); color: var(--accent);
display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.ls-step-icon .ls-icon { width: 15px; height: 15px; }
.ls-step-code { font-family: var(--mono-font); font-size: 0.68rem; color: var(--ink-soft); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ls-run-btn { flex-shrink: 0; background: var(--accent); color: #fff; border: none; border-radius: 5px; padding: 0.35rem 0.75rem;
font-size: 0.7rem; font-weight: 700; cursor: pointer; font-family: var(--mono-font); }
.ls-run-btn:hover { opacity: 0.88; }
.ls-run-btn:active { transform: scale(0.96); }
.ls-note { text-align: center; font-size: 0.8rem; color: var(--ink-soft); padding-top: 1.4rem; max-width: 560px; margin: 0 auto; }
.ls-note strong { color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
.infographic-localstorage *, .infographic-localstorage *::before, .infographic-localstorage *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="ls-wrap">
<div class="ls-badge-row">
<span class="ls-badge">
<span class="ls-icon" id="lsBadgeIcon"></span>
one storage per origin — http and https don't share
</span>
</div>
<div class="ls-panels">
<div class="ls-panel">
<div class="ls-panel-head">
<span class="ls-panel-title"><span class="ls-icon" id="lsStorageIcon"></span>Storage</span>
<button class="ls-reset-btn" id="lsResetBtn" type="button"><span class="ls-icon" id="lsResetIcon"></span>reset</button>
</div>
<div class="ls-table-head" id="lsTableHead">Storage {length: 0}</div>
<div class="ls-table-body" id="lsTableBody">
<div class="ls-empty">empty — run a step below</div>
</div>
</div>
<div class="ls-panel">
<div class="ls-panel-head">
<span class="ls-panel-title" style="color:#d4d4d4">>_ Console</span>
</div>
<div class="ls-console" id="lsConsole">
<div class="ls-console-empty">// output appears here</div>
</div>
</div>
</div>
<div class="ls-steps" id="lsSteps">
<div class="ls-step" data-step="0">
<span class="ls-step-num">1️⃣</span>
<span class="ls-step-icon"><span class="ls-icon" id="lsIcon0"></span></span>
<span class="ls-step-code">localStorage.setItem('theme', 'dark')</span>
<button class="ls-run-btn" data-run="0" type="button">Run</button>
</div>
<div class="ls-step" data-step="1">
<span class="ls-step-num">2️⃣</span>
<span class="ls-step-icon"><span class="ls-icon" id="lsIcon1"></span></span>
<span class="ls-step-code">console.log(localStorage.length)</span>
<button class="ls-run-btn" data-run="1" type="button">Run</button>
</div>
<div class="ls-step" data-step="2">
<span class="ls-step-num">3️⃣</span>
<span class="ls-step-icon"><span class="ls-icon" id="lsIcon2"></span></span>
<span class="ls-step-code">localStorage.getItem('theme')</span>
<button class="ls-run-btn" data-run="2" type="button">Run</button>
</div>
<div class="ls-step" data-step="3">
<span class="ls-step-num">4️⃣</span>
<span class="ls-step-icon"><span class="ls-icon" id="lsIcon3"></span></span>
<span class="ls-step-code">localStorage.removeItem('theme')</span>
<button class="ls-run-btn" data-run="3" type="button">Run</button>
</div>
<div class="ls-step" data-step="4">
<span class="ls-step-num">5️⃣</span>
<span class="ls-step-icon"><span class="ls-icon" id="lsIcon4"></span></span>
<span class="ls-step-code">for (key of Object.keys(localStorage)) ...</span>
<button class="ls-run-btn" data-run="4" type="button">Run</button>
</div>
</div>
<div class="ls-note">Each key also has a ✕ — click it to <strong>removeItem()</strong> that entry directly.</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-localstorage')
|| document.querySelector('.infographic-localstorage');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const ICONS = {
plus: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M8 12h8"/><path d="M12 8v8"/></svg>',
hash: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="9" y2="9"/><line x1="4" x2="20" y1="15" y2="15"/><line x1="10" x2="8" y1="3" y2="21"/><line x1="16" x2="14" y1="3" y2="21"/></svg>',
search: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>',
trash: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>',
repeat: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/></svg>',
database: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19A9 3 0 0 0 21 19V5"/><path d="M3 12A9 3 0 0 0 21 12"/></svg>',
refresh: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v6h-6"/></svg>',
lock: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>'
};
wrapper.querySelector('#lsBadgeIcon').innerHTML = ICONS.lock;
wrapper.querySelector('#lsStorageIcon').innerHTML = ICONS.database;
wrapper.querySelector('#lsResetIcon').innerHTML = ICONS.refresh;
wrapper.querySelector('#lsIcon0').innerHTML = ICONS.plus;
wrapper.querySelector('#lsIcon1').innerHTML = ICONS.hash;
wrapper.querySelector('#lsIcon2').innerHTML = ICONS.search;
wrapper.querySelector('#lsIcon3').innerHTML = ICONS.trash;
wrapper.querySelector('#lsIcon4').innerHTML = ICONS.repeat;
const tableHead = wrapper.querySelector('#lsTableHead');
const tableBody = wrapper.querySelector('#lsTableBody');
const consoleEl = wrapper.querySelector('#lsConsole');
const resetBtn = wrapper.querySelector('#lsResetBtn');
const runBtns = wrapper.querySelectorAll('[data-run]');
let store = {};
let orderKeys = [];
function renderTable() {
tableHead.textContent = 'Storage {length: ' + orderKeys.length + '}';
if (orderKeys.length === 0) {
tableBody.innerHTML = '<div class="ls-empty">empty \u2014 run a step below</div>';
return;
}
tableBody.innerHTML = '';
orderKeys.forEach(key => {
const row = document.createElement('div');
row.className = 'ls-row';
row.dataset.key = key;
row.innerHTML = '<span class="k">' + key + '</span><span class="sep">:</span>' +
'<span class="v">' + store[key] + '</span>' +
'<button class="del" data-key="' + key + '" title="removeItem(\'' + key + '\')">\u2715</button>';
tableBody.appendChild(row);
});
tableBody.querySelectorAll('.del').forEach(btn => {
btn.addEventListener('click', () => removeKey(btn.dataset.key, true));
});
}
function logLine(text, cls) {
if (consoleEl.querySelector('.ls-console-empty')) consoleEl.innerHTML = '';
const line = document.createElement('div');
line.className = 'ls-console-line ' + (cls || '');
line.textContent = text;
consoleEl.appendChild(line);
consoleEl.scrollTop = consoleEl.scrollHeight;
}
function setKey(key, value) {
if (!(key in store)) orderKeys.push(key);
store[key] = value;
renderTable();
}
function removeKey(key, echoIt) {
if (!(key in store)) return;
const row = tableBody.querySelector('.ls-row[data-key="' + key + '"]');
if (row) row.classList.add('is-leaving');
delete store[key];
orderKeys = orderKeys.filter(k => k !== key);
if (echoIt) {
logLine("> localStorage.removeItem('" + key + "');", 'is-echo');
logLine('undefined', 'is-result');
}
setTimeout(renderTable, row ? 180 : 0);
}
const RUNS = [
() => {
logLine("> localStorage.setItem('theme', 'dark');", 'is-echo');
setKey('theme', 'dark');
logLine('undefined', 'is-result');
},
() => {
logLine('> console.log(localStorage.length);', 'is-echo');
logLine(String(orderKeys.length), 'is-result');
},
() => {
logLine("> localStorage.getItem('theme');", 'is-echo');
const val = store.theme;
logLine(val === undefined ? 'null' : "'" + val + "'", 'is-result');
},
() => {
removeKey('theme', true);
},
() => {
logLine("> localStorage.setItem('theme', 'light');", 'is-echo');
setKey('theme', 'light');
logLine("> localStorage.setItem('backgroundColor', 'white');", 'is-echo');
setKey('backgroundColor', 'white');
logLine("> localStorage.setItem('color', '#111');", 'is-echo');
setKey('color', '#111');
logLine('> for (let key of Object.keys(localStorage)) { console.log(`${key}: ${localStorage.getItem(key)}`); }', 'is-echo');
orderKeys.forEach(key => {
logLine(key + ': ' + store[key], 'is-result');
});
}
];
runBtns.forEach(btn => {
btn.addEventListener('click', () => {
RUNS[Number(btn.dataset.run)]();
});
});
resetBtn.addEventListener('click', () => {
store = {};
orderKeys = [];
renderTable();
consoleEl.innerHTML = '<div class="ls-console-empty">// output appears here</div>';
});
renderTable();
})();
</script>Each website gets its own separate localStorage, based on:
Example:
https://example.com and http://example.com have separate storages.
You can access localStorage like this:
localStorageIt is a Storage object, so it has methods like setItem, getItem, removeItem, and properties like length.
Typing this in the console:
localStoragegives something like:
Storage {length: 0}1️⃣ Set a value — setItem()
localStorage.setItem('theme', 'dark');Stores a key–value pair.
2️⃣ Check number of items — length
console.log(localStorage.length);localStorage.length → shows how many items are stored.
3️⃣ Get a value — getItem()
localStorage.getItem('theme');Returns the value for the key theme.
4️⃣ Remove an item — removeItem()
localStorage.removeItem('theme');Deletes the theme entry.
5️⃣ Loop through all keys
localStorage.setItem('theme', 'light');
localStorage.setItem('backgroundColor', 'white');
localStorage.setItem('color', '#111');
let keys = Object.keys(localStorage);
for (let key of keys) {
console.log(`${key}: ${localStorage.getItem(key)}`);
}color: #111
theme: light
backgroundColor: white♨️ Storing Objects
localStorage stores only strings.
To store objects → convert to string:
const settings = {
backgroundColor: '#fff',
color: '#111',
theme: 'light'
};
localStorage.setItem('settings', JSON.stringify(settings));Output stored:
'{"backgroundColor":"#fff","color":"#111","theme":"light"}'To get it back as an object:
let storedSettings = JSON.parse(localStorage.getItem('settings'));
console.log(storedSettings);🆚 localStorage vs cookies
🔢 No. | 🧠 Feature | 📦 | 🍪 Cookies |
|---|---|---|---|
1. | Storage Limit | Up to 5MB | Up to 4KB |
2. | Data Sent with Requests | ❌ Not sent to the server with every request | ✅ Automatically sent to the server |
3. | Access Control | Only accessible via JavaScript on the client | Accessible via both client-side JavaScript and server |
4. | Management | Managed only by the browser (JavaScript) | Managed by both browser and server |
🎫 The storage event
When you make a change to the
Storageobject, thestorageevent is fired on the document.The
storageevent occurs in the following scenarios :Store a name-value pair by calling the
setItem()method.Remove a name-value pair by calling the
removeItem()method.And remove all values by calling the
clear()method.
The
storageevent has the following properties :domain– the domain which the storage changes for.newValue– the value that the key was set to ornullif the key was removed.oldValue– the value before the key was set or removed.key– the key that was set or removed.
To listen for the
storageevent, you use theaddEventListener()method of thewindowobject like this :
addEventListener('storage', function(e){
console.log(`The value of the ${e.key} changed for the ${e.domain}.`);
});