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.

Install

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

Peer dependencies: Vue ≥ 3.3.0

Quick Start

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>

API Reference

createSyncraft()

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>

Primary composable for Syncraft Labs in Vue.

ParameterTypeDescription
keystringUnique IndexedDB storage key
optionsUseSyncOptions<T>Configuration object

UseSyncOptions<T>

OptionTypeDefaultDescription
initialStateTundefinedDefault state when IndexedDB is empty
fetcher() => Promise<T>undefinedFetch initial data from remote source
pusher(entries: OutboxEntry<T>[]) => Promise<void>undefinedPush pending mutations to server
syncIntervalnumber5000Background sync interval (ms)
maxOutboxSizenumber1000Maximum outbox entries before overflow strategy triggers
overflowStrategy"reject" | "dropOldest" | "forceFlush""reject"Behavior when maxOutboxSize is reached
onOverflow(info: OutboxOverflowInfo) => void | Promise<void>undefinedCallback invoked on outbox overflow events
storageMode"document" | "collection""document"Storage strategy ("document" or "collection")
idFieldstringundefinedProperty name of entity ID (required for collection mode)

UseSyncReturn<T>

All reactive values are returned as Vue Refs:

PropertyTypeDescription
dataShallowRef<T | undefined>Current state (undefined during hydration)
update(updater: DraftUpdater<T>) => voidMutate state with draft (fire-and-forget)
refetch() => Promise<void>Pull fresh data via fetcher
isHydratingRef<boolean>true while loading from IndexedDB
isSyncingRef<boolean>true while pusher/refetch is running
isOfflineRef<boolean>true when navigator.onLine is false
errorShallowRef<Error | null>Last error from set/pusher/refetch
destroyStore() => voidDestroy the store for this key

destroyStore(registry, key): void

Destroy a store and remove it from the specified registry.