Skip to main content
Local-First Store

IDB-backed collection with CRUD, reactive queries, and automatic Background Sync queuing.

No items
import { effect, enhance } from 'weblisk';
import { collection } from 'weblisk/data/store.js';

enhance('#demo-store', (el, { $ }) => {
  const todos = collection('demo-todos');
  const live = todos.live();
  let itemNum = 0;

  effect(() => {
    const items = live();
    if (items.length === 0) {
      $('#output-store').textContent = 'No items';
    } else {
      $('#output-store').textContent = items
        .map(i => `• ${i.text}`)
        .join('\n');
    }
  });

  $('#btn-store-add').addEventListener('click', async () => {
    itemNum++;
    await todos.put({
      text: `Task #${itemNum} — ${new Date().toLocaleTimeString()}`
    });
  });

  $('#btn-store-clear').addEventListener('click', async () => {
    const all = await todos.getAll();
    for (const item of all) await todos.delete(item.id);
  });
});
Sync Status

Reactive sync status, pending mutation count, and manual sync trigger.

Checking...
import { effect, enhance } from 'weblisk';
import {
  syncStatus, pendingCount,
  countPending, triggerSync
} from 'weblisk/pwa/offline.js';

enhance('#demo-sync-status', (el, { $ }) => {
  effect(() => {
    const status = syncStatus();
    const pending = pendingCount();
    $('#output-sync-status').textContent =
      `Sync Status: ${status}\nPending: ${pending} mutation(s)\n` +
      '\nMutations queue via Background Sync.\n' +
      'SW replays them when connectivity returns.';
  });

  $('#btn-sync-trigger').addEventListener('click', async () => {
    await triggerSync();
  });

  $('#btn-sync-count').addEventListener('click', async () => {
    const count = await countPending();
    $('#output-sync-status').textContent =
      `Pending mutations: ${count}`;
  });
});