State Architecture
Off Grid uses Zustand for state management with AsyncStorage persistence. State is split across five stores, each with a single responsibility.Store Overview
| Store | Purpose | Persistence |
|---|---|---|
| appStore | Models, settings, hardware info, gallery | ✅ Yes |
| chatStore | Conversations, messages, streaming state | ✅ Partial (conversations only) |
| projectStore | Custom system prompts (projects) | ✅ Yes |
| authStore | Passphrase lock state | ✅ Yes |
| whisperStore | Whisper model selection | ✅ Yes |
appStore
Path: src/stores/appStore.ts:122 Responsibilities:- Downloaded models (text, image, Whisper)
- Active model IDs
- Settings (temperature, context length, GPU config, image gen params)
- Device hardware info (RAM, CPU)
- Gallery (generated images metadata)
- Background generation state (progress, status, preview path)
Schema
Key Actions
Model Management:Persistence Strategy
downloadProgress(ephemeral download state)deviceInfo(recalculated on app start)isGeneratingImage,imageGenerationProgress,imageGenerationStatus(runtime state)
chatStore
Path: src/stores/chatStore.ts:48 Responsibilities:- Conversations (title, messages, metadata)
- Streaming state (current streaming message, thinking indicator)
- Message operations (add, update, delete)
Schema
Key Actions
Conversation Management:Persistence Strategy
projectStore
Path: src/stores/projectStore.ts Responsibilities:- Custom system prompts (called “projects”)
- Active project selection
Schema
Key Actions
authStore
Path: src/stores/authStore.ts Responsibilities:- Passphrase lock state
- Authentication status
Schema
whisperStore
Path: src/stores/whisperStore.ts Responsibilities:- Whisper model selection (Tiny, Base, Small)
- Transcription configuration
Schema
Service-Store Synchronization
Services update stores to maintain UI reactivity. The pattern is unidirectional: services write to stores, UI components read from stores.Pattern: Service Updates Store
Why Both Service State AND Store State?
Service state:- Maintains generation across component unmounts
- Holds complex objects not suitable for AsyncStorage persistence
- Allows services to communicate with each other
- Triggers React re-renders via Zustand subscriptions
- Persists critical fields to AsyncStorage
- Provides global access to UI components
Data Flow Diagram
Persistence Implementation
AsyncStorage Adapter
Zustand’spersist middleware uses createJSONStorage(() => AsyncStorage) to automatically save/restore state.
| Store | AsyncStorage Key |
|---|---|
| appStore | local-llm-app-storage |
| chatStore | local-llm-chat-storage |
| projectStore | project-storage |
| authStore | auth-storage |
| whisperStore | whisper-storage |
Migration Example
When adding new fields or changing defaults, use themerge option: