Skip to content

Storage Kit

v0.2.7State & DataVueReact

Reactive localStorage, sessionStorage, IndexedDB and cookies for Vue 3 (and React) — TTL, encryption, schema migrations, undo/redo, and cross-tab sync.

Storage Kit
Get started →
npm install vue-storage-kit@latest
01 — Purpose

When you'd reach for this

localStorage only stores strings, silently fills up without warning, and knows nothing about other open tabs — vue-storage-kit takes those quirks off your hands and adds what plain localStorage never had: entry lifetimes, encryption, cross-tab sync, and format migrations.

The same app is open in two tabs

A shopper adds something to the cart in one tab and expects to see it in the other without reloading the page. Changes to storage propagate between tabs instantly, not only after a refresh.

A draft shouldn't stick around forever

A forgotten form draft or a temporary access token shouldn't sit in storage indefinitely — an entry's lifetime expires on its own, and stale data gets cleared out the next time it's read.

Personal data shouldn't sit in plain text

Anyone can open the browser's dev tools and read storage contents as plain text — sensitive values can be kept encrypted instead of relying on nobody looking.

An old data shape meets a new app version

After an update, a user's browser might still hold data shaped for a previous version of the app — it gets converted to the current shape automatically, instead of crashing or silently losing data.

02 — Features

At a glance

Unified reactive API for all storages

Unified reactive API for all storages

useStorage works with localStorage, sessionStorage, IndexedDB, and in‑memory via a single interface. Reactive Refs for Vue and useSyncExternalStore for React automatically sync state with the chosen backend. Supports TTL, schema migrations, encryption, compression, signing, debounce/throttle, and history.

Schema migrations, TTL, and AES-GCM encryption

Schema migrations, TTL, and AES-GCM encryption

Version your data and auto‑apply migration chains (up/down) on structure changes. Set TTL for automatic removal of stale entries on read. Encrypt data via Web Crypto API (AES‑GCM) with PBKDF2 key derivation or a ready‑made CryptoKey, including key rotation.

Cross‑tab sync, undo/redo, and throttling

Cross‑tab sync, undo/redo, and throttling

Enable sync for instant change propagation via BroadcastChannel (with storage event fallback). Optional leader election (navigator.locks) resolves conflicts by last‑write‑wins. Built‑in history (history: n) provides undo/redo without extra code, while debounce/throttle control write frequency.

Deep integration with the Vue and React ecosystem

Deep integration with the Vue and React ecosystem

The Vue plugin sets a global prefix, target, serializer, and error handler. The Nuxt module auto‑imports all composables and makes useCookie SSR‑aware (H3 on the server). The React version uses useSyncExternalStore, sharing one engine with Vue. Separate entry points for Pinia, DevTools, and testing.

Performance, SSR, and DevTools

Performance, SSR, and DevTools

Async adapters (including IndexedDB) don’t block rendering; isReady lets you show a skeleton until loading. On the server, an in‑memory fallback is used. Built‑in DevTools inspector shows all instances, their values, TTL, and history, while the timeline logs every change, migration, and error.

Quota resilience and integrity checks

Quota resilience and integrity checks

When storage quota is exceeded, the library sweeps out TTL‑expired entries and retries the write, and evictOnQuota evicts the least‑used keys instead of handling QuotaExceededError by hand. HMAC signing detects when an entry was modified outside the library — via DevTools or a page script.

03 — Quick example

See how it works

A cache that expires itself

TTL stores the expiry right inside the value's envelope — an expired key is removed automatically on the next read, no manual timers.

otp-cache.ts
import { useStorage } from 'vue-storage-kit'

const {
  value: otp,
  expiry,
  remove,
} = useStorage('otp', {
  defaultValue: '',
  ttl: 5 * 60 * 1000, // 5 minutes
  onExpire: () => router.push('/login'),
})

console.log(expiry.value) // Date | null — when this key expires

Synced across tabs

With sync: true, a change in one tab reaches every other open tab instantly — no server, no manual subscriptions.

cart-sync.ts
import { useStorage } from 'vue-storage-kit'

const { value: cart } = useStorage('cart', {
  defaultValue: [] as CartItem[],
  sync: true,
})

// cart.value stays in sync across every open tab automatically

Stored data upgrades itself on release

A user on an old version opens the app — the migration chain brings their stored data up to the current schema and persists the result immediately, no manual version if/else.

migrations.ts
import { useStorage } from 'vue-storage-kit'

interface SettingsV3 {
  theme: 'light' | 'dark'
  locale: string
}

const { value: settings } = useStorage<SettingsV3>('settings', {
  defaultValue: { theme: 'light', locale: 'en' },
  version: 3,
  migrations: [
    { version: 2, up: (d: any) => ({ ...d, theme: d.darkMode ? 'dark' : 'light' }) },
    { version: 3, up: (d: any) => ({ ...d, locale: d.lang ?? 'en' }) },
  ],
  onMigrate: (from, to) => console.log(`Migrated settings ${from} → ${to}`),
})

// A v1 user with { darkMode: true } in storage reads
// { darkMode: true, theme: 'dark', locale: 'en' } — the migration chain
// runs and persists automatically, on the very first read.