Реактивные localStorage, sessionStorage, IndexedDB и cookies для Vue 3 (и React) — TTL, шифрование, миграции схемы, undo/redo и синхронизация между вкладками.

localStorage хранит только строки, тихо переполняется без предупреждения и ничего не знает про другие открытые вкладки — vue-storage-kit берёт эти особенности на себя и добавляет то, чего в самом localStorage никогда не было: срок жизни записи, шифрование, синхронизацию между вкладками и миграции формата.
Пользователь добавил товар в корзину в одной вкладке и должен увидеть это в другой без перезагрузки страницы. Изменения в хранилище распространяются между вкладками мгновенно, а не только после обновления страницы.
Забытая форма или временный токен доступа не должны лежать в хранилище до конца времён — срок жизни записи истекает сам, и устаревшие данные удаляются при следующем обращении.
Любой может открыть инструменты разработчика и посмотреть содержимое хранилища обычным текстом — конфиденциальные значения можно держать зашифрованными, а не полагаться на то, что туда никто не заглянет.
После обновления приложения в хранилище пользователя может лежать структура данных из прошлой версии — она приводится к актуальному виду автоматически, вместо падения или молчаливой потери данных.

useStorage работает с localStorage, sessionStorage, IndexedDB и in‑memory через единый интерфейс. Реактивные Ref для Vue и useSyncExternalStore для React автоматически синхронизируют состояние с выбранным бекендом. Поддерживаются TTL, миграции схем, шифрование, сжатие, подпись, debounce/throttle и история изменений.

Версионируйте данные и автоматически применяйте цепочки миграций (up/down) при изменении структуры. Устанавливайте TTL для автоматического удаления устаревших записей при чтении. Шифруйте данные через Web Crypto API (AES-GCM) с выводом ключа из пароля (PBKDF2) или готовым CryptoKey, включая ротацию ключей.

Включите sync для мгновенного распространения изменений через BroadcastChannel (с fallback на storage event). Опциональный лидер (navigator.locks) разрешает конфликты по принципу last‑write‑wins. Встроенная история (history: n) даёт undo/redo без дополнительного кода, а debounce/throttle контролируют частоту записи.

Vue-плагин задаёт глобальный префикс, target, сериализатор и обработчик ошибок. Nuxt-модуль автоимпортирует все композаблы и делает useCookie SSR‑совместимым (с H3 на сервере). React‑версия использует useSyncExternalStore, разделяя один движок с Vue. Отдельные entry points для Pinia, DevTools и тестирования.

Асинхронные адаптеры (включая IndexedDB) не блокируют рендеринг; isReady позволяет показать скелетон до загрузки. На сервере используется in‑memory fallback. Встроенный DevTools-инспектор показывает все экземпляры, их значения, TTL и историю, а таймлайн логирует каждое изменение, миграцию и ошибку.

При превышении квоты хранилища библиотека сама зачищает устаревшие по TTL записи и повторяет запись, а evictOnQuota вытесняет наименее используемые ключи вместо ручной обработки QuotaExceededError. Подпись данных через HMAC обнаруживает изменение записи в обход библиотеки — напрямую через DevTools.
TTL хранит время истечения прямо в конверте значения — просроченный ключ удаляется автоматически при следующем чтении, без ручных таймеров.
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 С sync: true изменение в одной вкладке мгновенно долетает до всех остальных открытых вкладок — без сервера и без ручной подписки.
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 Пользователь на старой версии открывает приложение — цепочка миграций сама доводит сохранённые данные до текущей схемы и сразу сохраняет результат, без ручного if/else по версии.
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.