Skip to content

@syncraft-labs/vue

@syncraft-labs/vue provides the useSync composable — giving your Vue 3 components instant writes, IndexedDB persistence, background sync, and offline support.

Built with shallowRef to avoid unnecessary deep reactivity overhead on proxy-managed state snapshots.

Terminal window
npm install @syncraft-labs/core @syncraft-labs/vue

Peer dependencies: Vue ≥ 3.3.0

Initialize the createSyncraft() plugin in your main entry file (required for app-level Store Registry isolation in Nuxt/SSR):

main.ts
import { createApp } from 'vue'
import { createSyncraft } from '@syncraft-labs/vue'
import App from './App.vue'
const app = createApp(App)
app.use(createSyncraft())
app.mount('#app')
App.vue
<script setup lang="ts">
import { useSync } from "@syncraft-labs/vue";
interface TodoState {
todos: Array<{ id: string; text: string; done: boolean }>;
}
const { data, update, isHydrating, isOffline, error } = useSync<TodoState>(
"todos",
{
initialState: { todos: [] },
},
);
function addTodo() {
update((draft) => {
draft.todos.push({
id: crypto.randomUUID(),
text: "New todo",
done: false,
});
});
}
function toggleTodo(id: string) {
update((draft) => {
const todo = draft.todos.find((t) => t.id === id);
if (todo) todo.done = !todo.done;
});
}
</script>
<template>
<p v-if="isHydrating">Loading from cache…</p>
<div v-else>
<p v-if="isOffline">You're offline — changes saved locally</p>
<p v-if="error">Error: {{ error.message }}</p>
<button @click="addTodo">Add Todo</button>
<ul>
<li v-for="t in data?.todos" :key="t.id">
<label>
<input
type="checkbox"
:checked="t.done"
@change="toggleTodo(t.id)"
/>
{{ t.text }}
</label>
</li>
</ul>
</div>
</template>

Vue plugin function that sets up the store registry via Vue’s provide mechanism.

import { createSyncraft } from '@syncraft-labs/vue';
app.use(createSyncraft());

useSync<T>(key, options): UseSyncReturn<T>

Section titled “useSync<T>(key, options): UseSyncReturn<T>”

Primary composable for Syncraft Labs in Vue.

Parameter Type Description
key string Unique IndexedDB storage key
options UseSyncOptions<T> Configuration object
Option Type Default Description
initialState T undefined Default state when IndexedDB is empty
fetcher () => Promise<T> undefined Fetch initial data from remote source
pusher (entries: OutboxEntry<T>[]) => Promise<void> undefined Push pending mutations to server
syncInterval number 5000 Background sync interval (ms)
selector (state: T | undefined) => R undefined Extract specific slice to prevent over-rendering
onSyncStart () => void undefined Fired when background sync starts
onSyncSuccess () => void undefined Fired when background sync succeeds
onSyncError (error: Error) => void undefined Fired when background sync fails
onRollback (error: Error, previousState: T | undefined) => void undefined Fired when local persistence fails and optimistic state reverts
logger SyncraftLogger console Injectable logger replacing hardcoded console calls
maxOutboxSize number 1000 Maximum outbox entries before overflow strategy triggers
overflowStrategy "reject" | "dropOldest" | "forceFlush" "reject" Behavior when maxOutboxSize is reached
onOverflow (info: OutboxOverflowInfo) => void | Promise<void> undefined Callback invoked on outbox overflow events
storageMode "document" | "collection" "document" Storage strategy ("document" or "collection")
idField string undefined Property name of entity ID (required for collection mode)

All reactive values are returned as Vue Refs:

Property Type Description
data ShallowRef<T | undefined> Current state (undefined during hydration)
update (updater: DraftUpdater<T>) => void Mutate state with draft (fire-and-forget)
refetch () => Promise<void> Pull fresh data via fetcher
isHydrating Ref<boolean> true while loading from IndexedDB
isSyncing Ref<boolean> true while pusher/refetch is running
isOffline Ref<boolean> true when navigator.onLine is false
error ShallowRef<Error | null> Last error from set/pusher/refetch
destroyStore () => void Destroy the store for this key

Destroy a store and remove it from the specified registry.