Storage and offline support
Updated: Sep 19, 2026
Meta Ray-Ban Display glasses support localStorage and sessionStorage for small
string values:
localStorage can persist across page sessions.sessionStorage belongs to the current browsing-context session.
Both APIs are synchronous. Keep values small, catch write failures, and treat
stored values as recoverable rather than permanent. Browser data clearing, application removal,
device reset, or storage pressure can remove data.
function savePreferences(preferences) {
try {
localStorage.setItem('example.preferences', JSON.stringify(preferences));
return true;
} catch (error) {
console.warn('Could not save preferences:', error);
return false;
}
}
function loadPreferences() {
const stored = localStorage.getItem('example.preferences');
if (stored === null) {
return null;
}
try {
return JSON.parse(stored);
} catch (error) {
localStorage.removeItem('example.preferences');
return null;
}
}
Use removeItem() to remove one key. Reserve clear() for an origin whose
complete Web Storage namespace the app intentionally owns, because it deletes
all values on that origin. Apps hosted on the same origin can share site data, so namespace keys.
Use sessionStorage for temporary state that belongs only to the current page
session:
sessionStorage.setItem('example.onboardingSeen', 'true');
Handle QuotaExceededError instead of relying on a fixed quota.
Use Web Storage for preferences and small application state. Choose a more
appropriate storage and security design for images, large datasets, secrets,
bearer tokens, or sensitive personal information.
A Service Worker and Cache Storage can serve an installed app shell during an
offline cold start. The first visit still requires a network connection, and a
new worker may not control the page until a later navigation or reload.
Service Workers require a secure context such as https://. They are
event-driven and can be stopped between events rather than running continuously
in the background.
Use a path relative to the app instead of assuming it is hosted at the origin
root:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js').catch((error) => {
console.warn('Offline support is unavailable:', error);
});
}
Registration success does not guarantee that installation, activation, or every
cache write will succeed. Keep the app usable online when registration fails.
Use an app-specific cache prefix. During cleanup, delete only previous versions
owned by this app:
const CACHE_PREFIX = 'example-app-';
const CACHE_NAME = `${CACHE_PREFIX}v1`;
const APP_SHELL = [
new URL('./', self.registration.scope).href,
new URL('./app.js', self.registration.scope).href,
new URL('./styles.css', self.registration.scope).href,
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL)),
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((names) =>
Promise.all(
names
.filter(
(name) => name.startsWith(CACHE_PREFIX) && name !== CACHE_NAME,
)
.map((name) => caches.delete(name)),
),
),
);
});
cache.addAll() fails the installation if any requested resource fails. Keep the
precache list small and verify every URL. Limit cleanup to the app’s cache prefix because another application or library
on the same origin may own other caches.
Intercept only the requests the worker owns. This example uses cached responses
first, goes to the network for a miss, and falls back to the cached app shell for
a failed same-origin navigation:
self.addEventListener('fetch', (event) => {
const request = event.request;
const url = new URL(request.url);
if (request.method !== 'GET' || url.origin !== self.location.origin) {
return;
}
event.respondWith((async () => {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(request);
if (cached) {
return cached;
}
try {
return await fetch(request);
} catch (error) {
if (request.mode === 'navigate') {
const shell = await cache.match(APP_SHELL[0]);
if (shell) {
return shell;
}
}
throw error;
}
})());
});
Choose a strategy per resource type. Cache-first works well for versioned static
assets. Network-first or stale-while-revalidate can be more appropriate for data
that changes. Cache authenticated or sensitive responses only when the app has a deliberate
security and invalidation design.
Changing the Service Worker file triggers an update check. A newly installed
worker commonly waits until pages controlled by the old worker close. Use
versioned cache names, keep old and new application code compatible during the
transition, and use immediate activation only when the app has a safe handoff plan.
Cached data can be evicted or cleared. Treat the cache as a performance and
offline aid, not the only copy of user-created data. Remove obsolete caches by
prefix and keep the app functional when a cache entry is missing.
Connectivity and live data
navigator.onLine and the online and offline events are connectivity hints.
Use actual request success and failure as the source of truth because these hints
do not prove that the app’s service or a particular request is reachable.
Check HTTP status as well as network exceptions:
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Request failed with ${response.status}`);
}
return await response.json();
} catch (error) {
showOfflineMessage('This content is not available right now.');
return null;
}
}
Show an offline or stale state only when it helps the user understand what can be
done. Show a connection indicator only where it helps explain available behavior.
Test storage and offline behavior
Before publishing, verify:
- first installation while online;
- a controlled reload after the worker takes control;
- an offline cold start with the cached shell;
- failure when a required precache URL is missing;
- a waiting-worker update and the next activation;
- cleanup of only the app’s obsolete cache versions;
- missing or evicted Web Storage and Cache Storage data;
QuotaExceededError and malformed stored values;- failed network requests and non-successful HTTP responses;
- recovery when connectivity returns; and
- that clearing or removing the app does not leave correctness dependent on
retained local data.