Your data never drifts away.
Driftless is a framework-agnostic offline-first sync library with conflict handling and visual sync states.
- Offline-first UX – queue actions offline and auto-sync when back online
- Persistent storage – built on IndexedDB with UUID-based IDs
- Flexible sync adapters – REST available today, GraphQL & gRPC planned
- Conflict resolution – configurable via
onConflicthandler, supports:- Last-write-wins
- Merge strategies
- Manual user prompt
- Visual states – built-in hooks/components to show offline, queued, syncing, success, conflict
- Framework-agnostic – works with vanilla JS/TS, React, Angular, Vue, Svelte
- Enterprise-ready (planned) – audit logs, compliance helpers
npm install driftless
# or
yarn add driftless
# or
pnpm add driftlessOptional framework bindings:
npm install @driftless/react
npm install @driftless/vue
npm install @driftless/angularimport { createSync } from 'driftless';
import { createRestAdapter } from 'driftless';
const adapter = createRestAdapter({ endpoint: '/api/orders' });
const sync = createSync({
adapter,
pollIntervalMs: 5000,
});
// Store offline actions
sync.store('order', { id: 1, status: 'pending' });
// Listen for sync events
sync.on('status', (status) => {
console.log('Sync status:', status);
// offline, queued, syncing, success, conflict
});import { useSync } from '@driftless/react';
function OrderButton() {
const { store, status } = useSync('orders', '/api/orders');
return (
<div>
<button onClick={() => store({ item: 'wifi-pass' })}>Buy WiFi</button>
<p>Status: {status}</p>
</div>
);
}Driftless supports custom resolution strategies via the onConflict API:
sync.onConflict((local, remote) => {
// Example: manual merge
return {
...remote,
notes: [...remote.notes, ...local.notes],
};
});If no handler is registered, Driftless emits a conflict event so the app can decide.
Driftless uses IndexedDB as its local storage engine.
All queued items are stored under the database name driftless-db in the object store queue.
You can inspect and debug the queue directly in your browser:
-
Open DevTools
- Chrome / Edge:
F12→ Application tab - Firefox:
F12→ Storage tab
- Chrome / Edge:
-
Navigate to IndexedDB
- Expand
IndexedDB→driftless-db→queue
- Expand
-
View queued items
- Each entry is a JSON object of type
StoreItem:{ "id": "8b31c7d2-4f42-4d77-bdb2-9245c35640e3", "type": "order", "payload": { "item": "wifi-pass" }, "createdAt": 1694600000000, "version": 1 } id: unique UUID generated when the item is queuedtype: logical namespace you passed tostore()payload: the actual data to synccreatedAt: timestamp (ms since epoch)version: incremented on every conflict resolution retry
- Each entry is a JSON object of type
-
Check sync flow
- When you go offline and call
store(), the item will appear here. - When you come back online and sync succeeds, the item will be removed.
- If there’s a conflict, the item will either:
- be retried with a new
version(ifonConflicthandled it), or - stay in the queue until you resolve it.
- be retried with a new
- When you go offline and call
-
Why inspect the queue?
- To debug issues where items don’t sync.
- To confirm Driftless is persisting data offline.
- To visualize how conflicts are retried and resolved.
driftless→ core library (framework-agnostic)@driftless/react→ React hooks + UI components@driftless/vue→ Vue composables@driftless/angular→ Angular service@driftless/adapters→ REST (available), GraphQL/Firebase/Supabase (planned)
- Airlines: Passenger buys WiFi offline, syncs later, conflict handled if purchased elsewhere
- Healthcare: Nurse logs vitals offline, syncs later, conflict if another nurse updated
- Logistics: Driver scans packages offline, syncs later, conflict if hub already processed
- Retail: Shopper adds items offline, cart merges smoothly when online
| Feature / Capability | Driftless | PouchDB + CouchDB Replication | RxDB | CRDTs (Yjs / Automerge) | Workbox + Background Sync | Firebase / Supabase Offline Features |
|---|---|---|---|---|---|---|
| Local queue of user actions | ✅ (IndexedDB, explicit queue) | ✅ (with CouchDB) | ✅ | ✅ (for CRDT-backed data) | ✅ (for certain SDKs, e.g. Firestore offline) | |
| UUID-based IDs / Versioning Metadata | ✅ | ✅ (via document revisions) | ✅ | ✅ | ❌ | ✅ (depending on backend) |
| Conflict detection / customizable conflict resolution | ✅ (via onConflict / event) |
✅ (manual resolution, CouchDB revision merge) | ✅ (some support) | ✅ (CRDT merges automatically) | ❌ / Very limited | |
| Visibility & Inspectability of queued items | ✅ (can inspect IndexedDB queue) | Partial (replication logs, tools) | ✅ (reactive local DB) | ❌ / not for raw actions | ❌ (service worker under-the-hood) | Partial |
| Lightweight footprint | ✅ (designed to be minimal core + adapters) | Heavier (bundle + server replication support) | Heavier (reactivity, plugin layers) | Medium (for CRDT operations) | Lightweight for caching; lacks full conflict logic | Variable; SDKs tend to include a lot of features |
| Framework-agnostic | ✅ | ✅ | ✅ | Mostly, if adapted | ✅ for caching; data sync usually integrated per-framework | SDK tied, some frameworks only |
| Dependency on backend choices | Low — any backend that can respond with ok / conflict works |
High — you typically need CouchDB or compatible server | Medium/High — replication plugin/backends required | High — need CRDT-aware servers or peers | Low for caching; high for data consistency | Medium — depends on backend features and SDK availability |
| UI state hooks / status support | ✅ (offline, queued, syncing, conflict, success) | Partial / custom | ✅ (if you build) | ❌ / needs extra work | ❌ (mostly invisible to UI) | ✅ (some SDKs provide) |
| Compliance / audit log friendliness | ✅ (versioning + metadata) | Medium (replication logs, but restructuring needed) | Medium | Medium-Low | Low | High (stores persistent data + metadata) |
-
Where Driftless excels:
- Clear, inspectable local queue (IndexedDB)
- Conflict resolution via
onConflict+ custom merge - Lightweight, framework-agnostic API
- Good metadata (UUIDs, versions) for auditing
-
Where alternatives may be better:
- If you already use CouchDB (PouchDB) and need live replication across devices
- If you need real-time collaborative editing (CRDTs like Yjs or Automerge)
- If your backend / product already includes offline SDKs (Firebase, Supabase) and you want to leverage those
- Core offline queue + retry (IndexedDB)
- REST adapter
- Conflict resolution strategies
- React/Vue/Angular bindings
- UI components for status & conflict dialogs
- GraphQL adapter
- Cloud sync connectors (Firebase, Supabase)
- Enterprise features (audit logs, compliance)
v0.1.0
- Initial release
- IndexedDB queue with UUIDs
- REST adapter
- Conflict resolution API
- React hook (
useSync)
Contributions are welcome!
Please check the issues tab and open a discussion before working on large changes.
MIT © Driftless Contributors
