ADR-0019: Zustand for State Management
Status
Accepted - 2025-01-26
Context
TVL Platform needs client-side state management for UI state (modals, filters, selected items) that persists across components.
Decision
Zustand for lightweight, hook-based state management.
Rationale
- Minimal Boilerplate: No providers, actions, or reducers
- Small Bundle: ~1KB (vs Redux ~10KB)
- TypeScript: Excellent type inference
- React Integration: Uses hooks naturally
- DevTools: Redux DevTools compatible
Alternatives Considered
Alternative 1: Redux Toolkit
Rejected - Too much boilerplate, overkill for MVP
Alternative 2: Context API
Rejected - Performance issues (re-renders entire tree)
Alternative 3: Jotai
Rejected - Atomic approach less familiar to team
Usage Examples
1. Create Store
// stores/bookingStore.ts
import { create } from 'zustand';
interface BookingStore {
  selectedBookingId: string | null;
  selectBooking: (id: string | null) => void;
  filters: { status: string; dateRange: [Date, Date] | null };
  setFilters: (filters: Partial<BookingStore['filters']>) => void;
}
export const useBookingStore = create<BookingStore>((set) => ({
  selectedBookingId: null,
  selectBooking: (id) => set({ selectedBookingId: id }),
  filters: { status: 'all', dateRange: null },
  setFilters: (filters) => set((state) => ({
    filters: { ...state.filters, ...filters }
  }))
}));
2. Use in Components
import { useBookingStore } from '@/stores/bookingStore';
export function BookingsList() {
  const { selectBooking, filters } = useBookingStore();
  return (
    <div>
      {bookings.map(booking => (
        <div onClick={() => selectBooking(booking.id)}>
          {booking.guestName}
        </div>
      ))}
    </div>
  );
}
export function BookingFilters() {
  const { filters, setFilters } = useBookingStore();
  return (
    <select
      value={filters.status}
      onChange={(e) => setFilters({ status: e.target.value })}
    >
      <option value="all">All</option>
      <option value="pending">Pending</option>
      <option value="confirmed">Confirmed</option>
    </select>
  );
}
3. Persist to LocalStorage
import { persist } from 'zustand/middleware';
export const useBookingStore = create(
  persist<BookingStore>(
    (set) => ({
      // ... store definition
    }),
    { name: 'booking-store' }
  )
);
When to Use Zustand vs Server State
Use Zustand for:
- ✅ UI state (modals, tabs, filters)
- ✅ Selected items (checkboxes, active row)
- ✅ Form drafts (local storage)
Use TanStack Query for:
- ✅ Server data (bookings, properties)
- ✅ Caching, refetching, optimistic updates