Skip to main content

State Management

Beyond basic signals, Weblisk provides specialized state modules for common patterns.

Form State

Declarative form handling with validation and submission tracking:

js
import { formState } from 'weblisk/state/form.js';

const form = formState({
  fields: {
    email:    { required: true, pattern: /^[^@]+@[^@]+$/ },
    password: { required: true, minLength: 8 },
  },
  async onSubmit(values) {
    await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(values),
    });
  },
  onError(err) {
    console.error('Submit failed:', err);
  },
});

// Bind to a DOM form
form.bind('form#login');

// Or read state reactively
effect(() => {
  console.log('Valid:', form.valid());
  console.log('Submitting:', form.submitting());
  console.log('Errors:', form.errors());
});

FormState API

| Property/Method | Type | Description |

|----------------|------|-------------|

| values | signal | Current field values |

| errors | signal | Validation errors |

| submitting | signal | true during submit |

| submitted | signal | true after successful submit |

| submitError | signal | Last submit error |

| valid | computed | true when all fields pass validation |

| dirty | computed | true when any field has changed |

| setValue(name, value) | method | Set a field value |

| submit() | method | Trigger form submission |

| reset() | method | Reset all fields to initial values |

| bind(form) | method | Bind to a form element |

Undo / Redo

Wrap any signal with an undo/redo stack:

js
import { undoable, undoKeyboard } from 'weblisk/state/history.js';

const [text, setText, { undo, redo, canUndo, canRedo }] = undoable('', {
  maxHistory: 100,
});

setText('Hello');
setText('Hello World');

undo();          // text() === 'Hello'
redo();          // text() === 'Hello World'
canUndo();       // true
canRedo();       // false

// Install Cmd/Ctrl+Z shortcuts
const cleanup = undoKeyboard({ undo, redo });

Cross-Tab Sync

Signals that synchronize across all open tabs:

js
import { synced } from 'weblisk/state/sync.js';

const [theme, setTheme] = synced('theme', 'light');

setTheme('dark');
// → All other tabs with synced('theme') update to 'dark'

Uses BroadcastChannel with localStorage fallback.

IndexedDB Signals

For large data that exceeds localStorage's 5MB limit:

js
import { idbSignal } from 'weblisk/state/idb.js';

const [draft, setDraft, { ready, remove }] = idbSignal('editor-draft', '');

// Wait for IDB to load the persisted value
await ready;

setDraft(largeContent); // Debounced write to IDB (300ms default)

// Clean up
await remove();