# Svelte 5 State Management Guide ## Overview Svelte 5 provides multiple patterns for state management, from local component state to global application state. Choose the right pattern based on your needs. --- ## 📊 State Management Decision Tree ``` Is the state used by only one component? ├── YES → Use local $state() └── NO → Is it shared between parent/child? ├── YES → Pass as props or use $bindable() └── NO → Is it shared across unrelated components? ├── YES → Use a .svelte.ts store └── NO → Is it server data that needs caching? └── YES → Use SvelteKit load functions ``` --- ## 🏠 Pattern 1: Local Component State For state that belongs to a single component. ```svelte

Count: {count}

Average: {average.toFixed(2)}

``` --- ## 📤 Pattern 2: Props Down, Events Up Classic unidirectional data flow. ```svelte
``` --- ## 🔗 Pattern 3: Two-Way Binding with $bindable For controlled form components. ```svelte ``` --- ## 🏪 Pattern 4: Global Stores (.svelte.ts files) For state shared across unrelated components. ```typescript // stores/auth.svelte.ts export interface User { id: string; name: string; email: string; avatar?: string; } class AuthStore { user = $state(null); loading = $state(false); error = $state(null); isAuthenticated = $derived(this.user !== null); async login(email: string, password: string) { this.loading = true; this.error = null; try { const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); if (!response.ok) { throw new Error('Login failed'); } this.user = await response.json(); } catch (e) { this.error = e instanceof Error ? e.message : 'Unknown error'; throw e; } finally { this.loading = false; } } async logout() { await fetch('/api/auth/logout', { method: 'POST' }); this.user = null; } async checkSession() { try { const response = await fetch('/api/auth/me'); if (response.ok) { this.user = await response.json(); } } catch { this.user = null; } } } export const auth = new AuthStore(); ``` ```svelte ``` --- ## 🛒 Pattern 5: Complex Store with Actions For stores with complex business logic. ```typescript // stores/cart.svelte.ts export interface CartItem { id: string; name: string; price: number; quantity: number; } class CartStore { items = $state([]); // Derived values itemCount = $derived( this.items.reduce((sum, item) => sum + item.quantity, 0) ); subtotal = $derived( this.items.reduce((sum, item) => sum + item.price * item.quantity, 0) ); tax = $derived(this.subtotal * 0.1); total = $derived(this.subtotal + this.tax); isEmpty = $derived(this.items.length === 0); // Actions addItem(product: { id: string; name: string; price: number }) { const existing = this.items.find(item => item.id === product.id); if (existing) { existing.quantity++; } else { this.items.push({ ...product, quantity: 1 }); } this.persist(); } removeItem(id: string) { const index = this.items.findIndex(item => item.id === id); if (index !== -1) { this.items.splice(index, 1); this.persist(); } } updateQuantity(id: string, quantity: number) { const item = this.items.find(item => item.id === id); if (item) { if (quantity <= 0) { this.removeItem(id); } else { item.quantity = quantity; this.persist(); } } } clear() { this.items = []; this.persist(); } // Persistence private persist() { if (typeof localStorage !== 'undefined') { localStorage.setItem('cart', JSON.stringify($state.snapshot(this.items))); } } hydrate() { if (typeof localStorage !== 'undefined') { const saved = localStorage.getItem('cart'); if (saved) { try { this.items = JSON.parse(saved); } catch { this.items = []; } } } } } export const cart = new CartStore(); ``` ```svelte

Subtotal: ${cart.subtotal.toFixed(2)}

Tax: ${cart.tax.toFixed(2)}

Total: ${cart.total.toFixed(2)}

``` --- ## 🌐 Pattern 6: Context for Component Trees Pass state down a component tree without prop drilling. ```svelte {@render children()} ``` ```svelte ``` --- ## 📡 Pattern 7: Async State with Loading/Error Handle async operations elegantly. ```typescript // stores/async.svelte.ts export type AsyncState = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: Error }; export function createAsyncState() { let state = $state>({ status: 'idle' }); return { get state() { return state; }, get isLoading() { return state.status === 'loading'; }, get isSuccess() { return state.status === 'success'; }, get isError() { return state.status === 'error'; }, get data() { return state.status === 'success' ? state.data : null; }, get error() { return state.status === 'error' ? state.error : null; }, async run(fn: () => Promise) { state = { status: 'loading' }; try { const data = await fn(); state = { status: 'success', data }; return data; } catch (e) { const error = e instanceof Error ? e : new Error(String(e)); state = { status: 'error', error }; throw error; } }, reset() { state = { status: 'idle' }; } }; } // Usage in a store class UserStore { private usersAsync = createAsyncState(); get users() { return this.usersAsync.data ?? []; } get loading() { return this.usersAsync.isLoading; } get error() { return this.usersAsync.error; } async fetchUsers() { await this.usersAsync.run(async () => { const res = await fetch('/api/users'); return res.json(); }); } } ``` --- ## 🔄 Pattern 8: Undo/Redo State Maintain history for undo/redo functionality. ```typescript // stores/history.svelte.ts export function createHistoryState(initial: T) { let past = $state([]); let present = $state(initial); let future = $state([]); return { get value() { return present; }, get canUndo() { return past.length > 0; }, get canRedo() { return future.length > 0; }, set(value: T) { past.push($state.snapshot(present) as T); present = value; future = []; }, undo() { if (past.length === 0) return; future.unshift($state.snapshot(present) as T); present = past.pop()!; }, redo() { if (future.length === 0) return; past.push($state.snapshot(present) as T); present = future.shift()!; }, reset(value: T) { past = []; present = value; future = []; } }; } // Usage const editor = createHistoryState({ text: '', fontSize: 16 }); // Make changes editor.set({ text: 'Hello', fontSize: 16 }); editor.set({ text: 'Hello World', fontSize: 16 }); // Undo editor.undo(); // Back to 'Hello' editor.undo(); // Back to '' // Redo editor.redo(); // Forward to 'Hello' ``` --- ## 📋 State Management Best Practices ### ✅ Do: - Keep state as local as possible - Use `$derived` for computed values - Use `.svelte.ts` files for shared state - Type your state with TypeScript - Use `$state.snapshot()` before serializing - Clean up effects that create subscriptions ### ❌ Don't: - Create global stores for component-local state - Mutate derived values - Put async logic in `$effect` without cleanup - Destructure `$state` objects (breaks reactivity) - Forget to handle loading/error states --- ## 🔍 Debugging State ```svelte ```