vue-storage-kit
Reactive localStorage, sessionStorage, IndexedDB and cookies for Vue 3 (and React) — TTL, AES-GCM encryption, HMAC signing, schema migrations with up/down functions, undo/redo, and cross-tab sync — built on a framework-agnostic core, with Vue and React as thin bindings over it.
Features
- useStorage — unified reactive state over
localStorage,sessionStorage,IndexedDB, or an in-memory store; drop-in replacement for vueuseuseLocalStorage/useSessionStorage. Available for Vue (aRef) and React (auseSyncExternalStore-backed hook) — both are thin bindings over the same framework-agnostic engine - Schema migrations — versioned data with
up/downmigration chains; runs automatically on version mismatch, writes back the migrated value - TTL — optional time-to-live per key; lazy expiry checked on every read, no timers; manual
cleanExpired()sweep for startup cleanup - AES-GCM encryption — Web Crypto API (
crypto.subtle), key derived from a password via PBKDF2 or supplied as aCryptoKey; salt + IV + ciphertext packed into a single base64 string; derived key cached in session memory;reencrypt()/rotateEncryptedKey()to rotate a password without data loss - HMAC signing — lightweight accidental-corruption detection (
sign: { password }) for data that doesn't need to be secret; not a defense against a user editing their own browser's storage (see Corruption detection) — combine withencryptfor confidentiality + integrity - Undo / redo —
history: nkeeps the last n values in memory;undo()/redo()navigate them (not persisted across reloads) - Debounce & throttle —
debouncecoalesces writes after a pause;throttleguarantees a write at most every n ms during continuous changes (a slider, a drag) - Resilient writes — on
QuotaExceededError, sweeps this adapter's own expired-TTL entries and retries once; opt intoevictOnQuotato additionally evict the least-recently-written other keys. Non-quota write errors are reported viaonError, not thrown from inside a reactive callback - Cross-tab sync —
BroadcastChannelwithstorageevent fallback; last-write-wins conflict resolution by timestamp; optional leader election vianavigator.locks - useIndexedDB — promise-based key-value API plus a reactive
useIDBReffor a single key; or just passtarget: 'indexeddb'touseStorage()for the same TTL/migrations/encrypt/compress/sync pipeline as any other target - useCookie — reactive cookies with
expires,sameSite,secure; client-only from the package root, or SSR-aware (H3-backed on the server, supportshttpOnly) when auto-imported inside the Nuxt module - Vue plugin — global prefix, default target/serializer/encrypt, and a global error handler, all applied to every
useStorage()call - Nuxt module — auto-imports all composables; wires up the plugin with runtime config
- Serializer — JSON with round-trip support for
Date,Map,Set, andundefined; bring your own serializer via theSerializer<T>interface - SSR-safe — falls back to in-memory storage when
windowis unavailable;isReadyref lets components show a skeleton until hydration - Devtools — a custom Vue Devtools inspector and timeline over every live
useStorage()instance (Vue or React) — current value, target, TTL, undo/redo state, plus a log of write/expire/migrate/sync events;/devtoolsentry point, opt-in viasetupDevtools(app) - Testing utilities —
/testingentry point:mockStorage(),resetStorageState(),seedEnvelope()/seedExpiredEnvelope(),flushAsync()— the patterns this package's own test suite uses, packaged for your tests - Vue and React as optional peers —
@vue/devtools-apiis the sole required runtime dependency (used only if you callsetupDevtools); neithervuenorreactis required by the package itself, only by the entry point you actually import./crypto,/sync,/compress,/pinia,/devtools,/react,/testingare separate tree-shakeable entry points
Installation
bash
npm install vue-storage-kitFor Vue, install Vue itself (optional peer — only needed if you import from the package root or any Vue-specific composable):
bash
npm install vue@>=3.3For React (vue-storage-kit/react), install React instead — you don't need vue at all:
bash
npm install react@>=18Quick start
vue
<script setup lang="ts">
import { useLocalStorage } from 'vue-storage-kit'
const { value: theme } = useLocalStorage('theme', 'light')
</script>
<template>
<button @click="theme = theme === 'light' ? 'dark' : 'light'">Current theme: {{ theme }}</button>
</template>The value is persisted to localStorage and is reactive — changing theme.value writes to storage immediately.