State Management
Beyond basic signals, Weblisk provides specialized state modules for common patterns.
Form State
Declarative form handling with validation and submission tracking:
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:
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:
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:
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();