# Alpine.js Patterns in Bookhoard ## Book Picker Modal Pattern (SSR + HTMX + Alpine.store) For modals that need state persistence across HTMX swaps: ### TypeScript ```typescript // web/src/bookPicker.ts Alpine.store("bookPicker", { isOpen: false, selectedBooks: new Set(), open() { this.isOpen = true; this.selectedBooks.clear(); this.loadBooks(); }, close() { this.isOpen = false; this.selectedBooks.clear(); }, toggleBook(id: string) { if (this.selectedBooks.has(id)) { this.selectedBooks.delete(id); } else { this.selectedBooks.add(id); } // Trigger reactivity this.selectedBooks = new Set(this.selectedBooks); }, isSelected(id: string): boolean { return this.selectedBooks.has(id); }, get selectedCount(): number { return this.selectedBooks.size; } }); Template
Key Principles - ✅ Alpine.store persists state across HTMX DOM swaps - ✅ Server returns HTML with checkbox :checked attributes - ✅ No client-side rendering or manual DOM manipulation - ✅ HTMX handles dynamic content updates - ✅ Alpine manages UI state only Dropdown Pattern (Local x-data)
Best Practices - ✅ State lives in template (x-data) - ✅ UI updates automatically (x-show) - ✅ No manual DOM manipulation in TypeScript - ✅ Pure business logic only in TypeScript functions - ✅ Use Alpine.store for global state - ✅ Use local x-data for component state - ✅ Use x-init for setup only (no data fetch for SSR pages) - ❌ Never use classList in TypeScript - ❌ Never use getElementById for show/hide - ❌ Never fetch data in x-init (for SSR pages)