Skip to content

Reactive Storage

useStorage() — the core composable. Works with localStorage, sessionStorage, and an in-memory fallback.

ts
useStorage<T>(key: string, options: StorageOptions<T>): UseStorageReturn<T>

Options

defaultValue

T

Value returned when the key is absent or has expired.

target

'local' | 'session' | 'memory' | 'indexeddb' · default: 'local'

Storage backend. 'indexeddb' stores through a single dedicated object store (db vue-storage-kit, store kv) — for custom databases/stores or secondary indexes, use useIndexedDB() / useIDBRef() instead.

ttl

number

Time-to-live in milliseconds; 0 or omitted = no expiry.

version

number · default: 1

Schema version of the stored data.

migrations

Migration[] · default: []

Migration functions run when stored version differs from version — see Schema migrations.

encrypt

boolean | EncryptOptions · default: false

Enable AES-GCM encryption — see Encryption.

compress

boolean | CompressOptions · default: false

Compress the stored envelope via the Compression Streams API. Applied before encrypt, so compression still works on the plaintext (compressed ciphertext yields no size benefit) — see Compression.

sign

boolean | SignOptions · default: false

HMAC-SHA256 check, applied as the outermost layer (wraps compressed/encrypted data too). Detects accidental corruption without requiring secrecy — not a defense against a user editing their own storage — see Corruption detection — combine with encrypt for both.

sync

boolean | SyncOptions · default: false

Enable cross-tab sync via BroadcastChannel — see Tab sync.

debounce

number

Coalesce writes: only persist debounce ms after the last change. Mutually exclusive with throttle (throttle wins if both are set).

throttle

number

Write at most once every throttle ms even during continuous changes, instead of only after they stop.

history

number

Keep up to this many past values in memory for undo()/redo(). Not persisted — resets on reload.

evictOnQuota

boolean | { max?: number } · default: false

On QuotaExceededError, if sweeping this adapter's expired-TTL entries isn't enough, evict its least-recently-written other keys (oldest first, up to max, default 1) and retry. Off by default — deleting unrelated keys is a real side effect.

serializer

Serializer<T> · default: JSON serializer

Custom serialize / deserialize pair.

onError

(err: StorageError) => void

Called instead of throwing on quota exceeded, parse errors, crypto errors, invalid signatures, or other write failures.

onExpire

(key: string) => void

Called when a TTL-expired key is removed on read.

onMigrate

(from: number, to: number) => void

Called after a successful migration.

Return value

value

Ref<T>

Reactive two-way binding; assigning writes to storage.

isReady

Ref<boolean>

false until the initial async read completes (important for IndexedDB and encrypted values).

error

Ref<StorageError | null>

Last error, null if none.

expiry

ComputedRef<Date | null>

When the key expires, null if no TTL.

canUndo

ComputedRef<boolean>

Whether undo() currently does anything (always false unless history is set).

canRedo

ComputedRef<boolean>

Whether redo() currently does anything (always false unless history is set).

remove

() => void

Delete the key from storage and reset value to defaultValue.

refresh

() => Promise<void>

Re-read from storage (useful if another process may have written).

undo

() => void

Navigate back through values recorded via history; a no-op if history isn't set or the undo stack is empty.

redo

() => void

Navigate forward through values recorded via history; a no-op if history isn't set or the redo stack is empty.

Examples

Basic read/write:

ts
const { value: counter } = useStorage('counter', { defaultValue: 0 })

counter.value++ // writes to localStorage immediately

Session storage:

ts
const { value: token } = useStorage('auth-token', {
  defaultValue: '',
  target: 'session',
})

TTL — auto-expire after 30 minutes:

ts
const { value: cache, expiry } = useStorage('search-cache', {
  defaultValue: [] as string[],
  ttl: 30 * 60 * 1000,
  onExpire: (key) => console.log(`${key} expired`),
})

console.log(expiry.value) // Date | null

Error handling:

ts
const { value, error } = useStorage('data', {
  defaultValue: {},
  onError: (err) => {
    if (err.type === 'quota-exceeded') showToast('Storage is full')
    if (err.type === 'parse-error') console.warn('Corrupted value, reset to default')
  },
})

Custom serializer:

ts
import type { Serializer } from 'vue-storage-kit'

const base64Serializer: Serializer<string> = {
  serialize: (v) => btoa(v),
  deserialize: (raw) => atob(raw),
}

const { value } = useStorage('encoded', {
  defaultValue: '',
  serializer: base64Serializer,
})

useLocalStorage / useSessionStorage

Shorthand composables — identical to useStorage but with target pre-set and defaultValue as the second argument (vueuse-compatible signature).

ts
useLocalStorage<T>(key: string, defaultValue: T, opts?): UseStorageReturn<T>
useSessionStorage<T>(key: string, defaultValue: T, opts?): UseStorageReturn<T>
ts
import { useLocalStorage, useSessionStorage } from 'vue-storage-kit'

const { value: settings } = useLocalStorage('settings', { theme: 'light', lang: 'en' })
const { value: draft } = useSessionStorage('draft', '')

These are drop-in replacements for @vueuse/core useLocalStorage / useSessionStorage.

defineStorageKey

A typed, reusable key descriptor — define a key's shape and options once, then pass the descriptor to useStorage() directly instead of repeating key/options at every call site.

ts
function defineStorageKey<T>(key: string, options: StorageOptions<T>): StorageKeyDef<T>

useStorage() accepts either the usual (key, options) pair or a single StorageKeyDef<T>:

ts
useStorage<T>(key: string, options: StorageOptions<T>): UseStorageReturn<T>
useStorage<T>(def: StorageKeyDef<T>): UseStorageReturn<T>
ts
import { defineStorageKey, useStorage } from 'vue-storage-kit'

// Define once, e.g. in a shared keys.ts module
const themeKey = defineStorageKey('theme', {
  defaultValue: 'light' as 'light' | 'dark',
  ttl: undefined,
})

// Use anywhere, fully typed, without repeating the options
const { value: theme } = useStorage(themeKey)