@gks101/localyx
v1.0.2
Published
A React hook for robust local storage state management with TTL and cross-tab sync.
Maintainers
Readme
@gks101/localyx
A lightweight (<1kb gzip+minified )React hook for persistent localStorage state with TTL expiry, same-tab and cross-tab sync, and optional encoding/serialization controls.
Installation
npm install @gks101/localyxQuick Start
import { useLocalStorageState } from '@gks101/localyx';
function ThemeToggle() {
const [theme, setTheme, clearTheme] = useLocalStorageState<'light' | 'dark'>('theme', 'dark');
return (
<div>
<p>Theme: {theme}</p>
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>Toggle</button>
<button onClick={clearTheme}>Reset</button>
</div>
);
}Features
- SSR-safe access guards.
- Same-tab and cross-tab synchronization.
- TTL expiry with automatic in-tab cleanup.
- Expiry strategies:
absoluteandsliding. onExpirelifecycle callback.- Optional namespaced keys.
- Custom serializer and encrypt/decrypt functions.
- Utility:
getRemainingTtl(...).
TTL Example (absolute + callback)
import { useLocalStorageState } from '@gks101/localyx';
function Session() {
const [token, setToken, clearToken] = useLocalStorageState<string | null>('session', null, {
ttl: 30 * 60 * 1000, // 30m
ttlStrategy: 'absolute',
onExpire: ({ key }) => {
console.log(`Expired key: ${key}`);
},
});
return (
<div>
<p>{token ?? 'No active token'}</p>
<button onClick={() => setToken(`token_${Date.now()}`)}>Login</button>
<button onClick={clearToken}>Logout</button>
</div>
);
}Sliding TTL Example
const [cache] = useLocalStorageState('search_cache', '', {
ttl: 5 * 60 * 1000,
ttlStrategy: 'sliding',
});sliding refreshes the timestamp on read/sync access so active values remain alive.
Namespaced Keys
const [user] = useLocalStorageState('profile', null, {
namespace: 'app:v1',
});This stores under app:v1:profile.
Remaining TTL Utility
import { getRemainingTtl } from '@gks101/localyx';
const remainingMs = getRemainingTtl('session', 30 * 60 * 1000, {
namespace: 'app:v1',
});Returns:
number(ms remaining),0when expired-but-not-yet-cleaned,nullwhen key is missing, not timestamped, or invalid.
Encryption / Serialization Notes
- Default behavior applies Base64 obfuscation.
- Use
encrypt: nullanddecrypt: nullto store plain JSON. - Base64 is not cryptographic encryption; provide custom crypto functions for real security.
API
useLocalStorageState<T>(key, initialValue, options?)
Returns [state, setState, removeValue].
options:
ttl?: numberttlStrategy?: 'absolute' | 'sliding'(default:'absolute')namespace?: stringonExpire?: (ctx: { key: string; value: T }) => voidnow?: () => numberencrypt?: ((raw: string) => string) | nulldecrypt?: ((raw: string) => string) | nullserializer?: { stringify: (v: T) => string; parse: (s: string) => T }
getRemainingTtl(key, ttl, options?)
options:
namespace?: stringdecrypt?: ((raw: string) => string) | nullparse?: (s: string) => { v: unknown; t?: number }now?: () => number
Production Patterns
- Auth/session token cache with hard expiry.
- Feature flag payload cache with short TTL.
- Local stale-while-revalidate snapshot cache.
Troubleshooting
- Value not syncing in same tab:
- ensure both hooks use the same key and namespace.
- Value not expiring:
- confirm
ttlis set and data was written by this hook.
- confirm
- Unexpected reset:
- invalid/corrupted payloads are wiped by design.
License
MIT
