Syncing Data
zod-vault handles bidirectional sync between your local store and the server.
Sync Methods
Section titled “Sync Methods”Full Sync
Section titled “Full Sync”await useStore.vault.sync();This:
- Pushes local changes to server
- Pulls remote changes from server
- Merges with local state (LWW)
Push Only
Section titled “Push Only”await useStore.vault.push();Send local changes to server without pulling.
Pull Only
Section titled “Pull Only”const hasChanges = await useStore.vault.pull();// Returns true if server had newer dataGet latest from server without pushing local changes.
Auto-Sync
Section titled “Auto-Sync”By default, vault syncs every 30 seconds:
vault(storeConfig, { name: "my-store", recoveryKey: "...", server: "https://...", getToken: () => client.getToken(), syncInterval: 30000, // 30 seconds (default)})Disable auto-sync:
vault(storeConfig, { // ... syncInterval: 0, // Manual sync only})Sync Status
Section titled “Sync Status”const status = useStore.vault.getSyncStatus();// "idle" | "syncing" | "synced" | "error" | "offline"React hook:
import { useVaultSync } from "@zod-vault/client";
function SyncIndicator() { const { status, hasPending, sync } = useVaultSync(useStore);
return ( <div> <span>Status: {status}</span> {hasPending && <span>Pending changes</span>} <button onClick={sync}>Sync Now</button> </div> );}Offline Support
Section titled “Offline Support”When offline, changes queue locally:
// Check for pending changesconst pending = useStore.vault.hasPendingChanges();// => true if offline queue has itemsWhen back online, call sync() to flush the queue.
Offline Queue Behavior
Section titled “Offline Queue Behavior”- Changes made offline are stored locally
- Data remains encrypted in localStorage
- On next
sync(), pending changes are pushed - Server applies changes with conflict resolution
Conflict Resolution
Section titled “Conflict Resolution”zod-vault uses Last-Write-Wins (LWW):
- Each change has a timestamp
- Server compares timestamps
- Newer timestamp wins
Example:
Device A: { count: 5 } at 10:00:00Device B: { count: 8 } at 10:00:05
Server keeps: { count: 8 } (newer)Handling Conflicts in Your App
Section titled “Handling Conflicts in Your App”For more control, use partialize to sync only specific fields:
vault(storeConfig, { name: "my-store", recoveryKey: "...", partialize: (state) => ({ // Only sync these fields notes: state.notes, settings: state.settings, // Don't sync temporary UI state }),})Sync on App Lifecycle
Section titled “Sync on App Lifecycle”Recommended pattern:
// On app startuseEffect(() => { useStore.vault.sync();}, []);
// On visibility change (tab focus)useEffect(() => { const handleVisibility = () => { if (document.visibilityState === "visible") { useStore.vault.sync(); } }; document.addEventListener("visibilitychange", handleVisibility); return () => document.removeEventListener("visibilitychange", handleVisibility);}, []);
// Before close (flush pending)useEffect(() => { const handleBeforeUnload = () => { if (useStore.vault.hasPendingChanges()) { useStore.vault.push(); } }; window.addEventListener("beforeunload", handleBeforeUnload); return () => window.removeEventListener("beforeunload", handleBeforeUnload);}, []);Debugging
Section titled “Debugging”Enable debug logging:
// In browser consolelocalStorage.setItem("zod-vault:debug", "true");Check sync state:
console.log({ status: useStore.vault.getSyncStatus(), pending: useStore.vault.hasPendingChanges(), hydrated: useStore.vault.hasHydrated(),});