Migration from persist()
Migrating from Zustand’s persist() to zod-vault’s vault() is straightforward.
Basic Migration
Section titled “Basic Migration”Before
Section titled “Before”import { create } from "zustand";import { persist } from "zustand/middleware";
const useStore = create( persist( (set) => ({ count: 0, increment: () => set((s) => ({ count: s.count + 1 })), }), { name: "my-store" } ));import { create } from "zustand";import { vault } from "@zod-vault/zustand";
const useStore = create( vault( (set) => ({ count: 0, increment: () => set((s) => ({ count: s.count + 1 })), }), { name: "my-store", recoveryKey: "ABCD-EFGH-...", // Add this } ));That’s it. Your data is now encrypted at rest.
Option Mapping
Section titled “Option Mapping”| persist() | vault() | Notes |
|---|---|---|
name | name | Same |
storage | storage | Compatible |
partialize | partialize | Same |
merge | merge | Same |
onRehydrateStorage | onRehydrateStorage | Same |
skipHydration | skipHydration | Same |
version | - | Server handles versioning |
migrate | - | Handle before vault() |
New Options
Section titled “New Options”| Option | Description |
|---|---|
recoveryKey | Required. Encryption key. |
server | Optional. Server URL for sync. |
getToken | Required if server set. |
syncInterval | Auto-sync interval (default 30s). |
API Changes
Section titled “API Changes”Hydration
Section titled “Hydration”// persist()useStore.persist.hasHydrated()useStore.persist.rehydrate()
// vault()useStore.vault.hasHydrated()useStore.vault.rehydrate()New Methods
Section titled “New Methods”useStore.vault.sync() // Bidirectional syncuseStore.vault.push() // Push to serveruseStore.vault.pull() // Pull from serveruseStore.vault.getSyncStatus() // Sync statususeStore.vault.hasPendingChanges() // Offline queueMigrating Existing Data
Section titled “Migrating Existing Data”When switching, existing unencrypted data won’t be readable.
Option 1: Fresh Start
Section titled “Option 1: Fresh Start”Just switch. Users start with empty state.
Option 2: Migration Script
Section titled “Option 2: Migration Script”// One-time migrationconst oldData = localStorage.getItem("my-store");if (oldData) { const parsed = JSON.parse(oldData); // Set state in new store useStore.setState(parsed.state); // Remove old data localStorage.removeItem("my-store");}Option 3: Parallel Stores
Section titled “Option 3: Parallel Stores”// Keep both temporarilyconst useLegacy = create(persist(...));const useStore = create(vault(...));
// Migrate on first loadif (useLegacy.getState().data && !useStore.vault.hasHydrated()) { useStore.setState(useLegacy.getState()); useLegacy.persist.clearStorage();}TypeScript
Section titled “TypeScript”Types work the same:
// Beforeimport type { PersistOptions } from "zustand/middleware";
// Afterimport type { VaultOptions } from "@zod-vault/zustand";Common Issues
Section titled “Common Issues””Decryption failed” on first load
Section titled “”Decryption failed” on first load”Expected when switching. Old unencrypted data can’t be decrypted. Use migration strategy above.
SSR Hydration
Section titled “SSR Hydration”Same as persist():
vault(config, { name: "my-store", recoveryKey: "...", skipHydration: true,});
// Client-sideuseStore.vault.rehydrate();