Architecture
Why Jotai?
Jotai was chosen for:- Atomic state: Small, focused state units instead of monolithic stores
- Minimal boilerplate: No actions, reducers, or selectors
- TypeScript-first: Excellent type inference
- React integration: Works naturally with hooks and Suspense
- Performance: Automatic dependency tracking and re-render optimization
State Organization
State is organized insrc/app/state/ with atoms grouped by domain:
Core State Atoms
Sessions (sessions.ts)
Manages multi-account authentication state:
- localStorage persistence via
atomWithLocalStorage - Multi-session support (multiple logged-in accounts)
- Active session tracking with
activeSessionIdAtom - Migration support from legacy single-session storage
Settings (settings.ts)
User preferences with localStorage persistence:
Modal State (modal.ts)
Global modal management:
Upload State (upload.ts)
Tracks file upload progress:
State Hooks
Custom hooks insrc/app/state/hooks/ provide convenient state access:
Settings Hook (hooks/settings.ts)
Provides granular setting updates:
Room List Hook (hooks/roomList.ts)
Manages room categorization and filtering:
Unread Hook (hooks/unread.ts)
Tracks unread messages and notifications:
Persistence
Local Storage Integration
TheatomWithLocalStorage utility provides automatic persistence:
State Patterns
Derived State
Use Jotai’s built-in derivation:Async State
Jotai supports async atoms naturally:Write-Only Atoms
For actions that don’t store state:Matrix Client State
While Jotai manages application state, Matrix SDK manages protocol state:- Room events: Stored in IndexedDB by matrix-js-sdk
- Sync state: Managed by SDK’s sync accumulator
- Crypto state: Handled by Rust crypto implementation
Best Practices
- Keep atoms focused: One concern per atom
- Use derived state: Don’t duplicate data
- Leverage TypeScript: Atoms infer types automatically
- Prefer hooks: Create custom hooks for complex state logic
- Persist thoughtfully: Only persist user preferences, not ephemeral state
- Test isolation: Atoms can be tested independently of components
Example: Creating New State
To add new application state:- Create the atom file in
src/app/state/:
- Create a hook if needed:
- Use in components: