We all love localStorage. It is the easiest way to persist state in a web app. You don’t need a database, you don’t need a backend, and the API is dead simple: setItem and getItem.
But if you’ve built enough production apps, you know localStorage has two major flaws that eventually come back to bite you:
It’s Immortal: Data stays there forever. Unless you manually write code to delete it, your user’s disk fills up with stale data from three years ago. 1.
It’s Exposed: Open the DevTools "Application" tab, and anyone can read (and edit) your application state in plain text.
I built a lightweight, zero-dependency TypeScript package to solve exactly these problems.
Meet @kushalst/local-storage-expiry
#…
We all love localStorage. It is the easiest way to persist state in a web app. You don’t need a database, you don’t need a backend, and the API is dead simple: setItem and getItem.
But if you’ve built enough production apps, you know localStorage has two major flaws that eventually come back to bite you:
It’s Immortal: Data stays there forever. Unless you manually write code to delete it, your user’s disk fills up with stale data from three years ago. 1.
It’s Exposed: Open the DevTools "Application" tab, and anyone can read (and edit) your application state in plain text.
I built a lightweight, zero-dependency TypeScript package to solve exactly these problems.
Meet @kushalst/local-storage-expiry
The Problem with "Forever" Storage
Imagine you cache a user’s profile to save an API call:
// Standard localStorage
localStorage.setItem('user', JSON.stringify({ name: 'Kushal', role: 'admin' }));
If the user’s role changes to editor on the server, but your frontend logic prioritizes localStorage, the user might see the old admin dashboard for months—until they clear their cache manually.
To fix this, you usually have to write messy wrapper code to save a timestamp, check it on every read, and handle parsing errors. It’s boilerplate city.
The Solution: Built-in TTL (Time-To-Live)
With @kushalst/local-storage-expiry, expiration is a first-class citizen. You set a duration (in milliseconds), and the library handles the rest.
Installation
npm i @kushalst/local-storage-expiry
Usage
The API is designed to feel exactly like the native one, just smarter.
import { set, get } from "@kushalst/local-storage-expiry";
// Cache this data for exactly 1 hour
set("userProfile", { id: 123, name: "Kushal" }, 60 * 60 * 1000);
// Later...
const profile = get("userProfile");
// Returns data if < 1 hour old.
// Returns null (and auto-deletes) if > 1 hour old.
If the data is expired, the library automatically cleans it up for you immediately upon access. No more stale UI bugs.
Feature Highlight: Obfuscation
This is where standard wrappers usually stop, but I wanted something better.
By default, localStorage saves plain strings. If you walk away from your computer, anyone can open DevTools and see exactly what is stored.
This library automatically obfuscates your data using a lightweight XOR + Base64 algorithm.
- Before (Standard):
"user": "{\"id\":123,\"name\":\"Kushal\"}" - After (With this library):
"lse_user": "eyJ2IjoxLCJlIjoxNzM1M... (gibberish) ..."
Is this military-grade encryption? No. (Never store passwords in localStorage!). But it prevents casual snooping and stops users from manually tampering with specific JSON fields to break your app state.
Feature Highlight: The Garbage Collector
"Lazy deletion" (deleting when you try to access the key) is great, but what about keys you never access again? They usually sit there, eating up your 5MB storage quota.
I included a flushExpired() utility that scans your storage and bulk-deletes dead keys.
import { flushExpired } from "@kushalst/local-storage-expiry";
// Run this on app startup (e.g., in useEffect or main.ts)
flushExpired();
This ensures your app keeps a small footprint on the user’s device, automatically removing junk from previous sessions.
Safety First: Namespacing
You might be worried: "Will flushExpired() delete data from other libraries or my other apps?"
No. The library automatically prefixes all keys with lse_
- set(‘token’) -> saves to lse_token
- clear() -> only wipes keys starting with lse_
Your other localStorage data is completely safe.
Wrapping Up
If you are looking for a robust, type-safe way to handle client-side caching without pulling in massive dependencies, give this a try. It works in React, Vue, Angular, Svelte, and even vanilla JS.
📦 NPM: npmjs.com/package/@kushalst/local-storage-expiry
Happy coding!