Client State Management
Comprehensive guide for managing client state with Zustand in our React Native application
Client State Management
Overview
This document outlines our approach to managing client-side state - data that exists exclusively within the application and doesn't need to be synchronized with external servers. Our architecture implements Zustand as the dedicated solution for all client state management, following our application's Golden Rule: "Client state belongs in Zustand, not in TanStack Query or Redux". This approach optimizes for simplicity, developer experience, and performance across the application.
Purpose & Scope
- Target Audience: React Native developers implementing features that require global state management, UI state coordination, or persistent local settings
- Problems Addressed: Simplifying state management, reducing boilerplate, improving TypeScript integration, and providing performance optimizations
- Scope Boundaries: Covers Zustand implementation, store patterns, performance optimization, and testing but does not cover server state management or data fetching strategies
Core Components/Architecture
Component Types
| Component | Responsibility | Implementation |
|---|---|---|
| Zustand Store | State container and update logic | Feature-specific store files |
| Store Actions | Methods that modify state | Defined within store creation |
| Selectors | Extract and subscribe to specific state slices | Used in components via hooks |
| Middleware | Add functionality to stores | Persistence, immutability, debugging |
| Store Hooks | Encapsulate store access patterns | Feature-specific custom hooks |
When to Use Client State
Zustand should be used for any data that meets these criteria:
-
(Do ✅) Use for UI state like modals, drawers, and navigation state
-
(Do ✅) Use for user preferences and settings that persist across sessions
-
(Do ✅) Use for temporary form data or wizard flows
-
(Do ✅) Use for cached data that doesn't require server synchronization
-
(Do ✅) Use for cross-component communication
-
(Don't ❌) Use for server data that requires synchronization
-
(Don't ❌) Use for form state in individual components
-
(Don't ❌) Use for ephemeral UI state in single components
Design Principles
Core Architectural Principles
-
(Do ✅) Keep stores small and focused
- Create separate stores for distinct concerns
- Avoid monolithic stores that handle multiple domains
-
(Do ✅) Use TypeScript interfaces for store definitions
- Define explicit types for all state and actions
- Leverage TypeScript for autocompletion and error checking
-
(Do ✅) Create selectors for performance optimization
- Select only the specific state slices components need
- Use stable references for selectors when appropriate
-
(Don't ❌) Mix server and client state in the same store
- Server state belongs in TanStack Query
- Client state belongs in Zustand
Store Organization Patterns
| Pattern | Used For | Implementation |
|---|---|---|
| Global Stores | Application-wide settings, UI state | Located in core/store/ |
| Feature Stores | Feature-specific state | Located in features/[feature]/state/ |
| Transient Stores | Temporary workflow state | Created and destroyed with features |
| Persistent Stores | Settings, auth tokens, preferences | Implemented with persist middleware |
Implementation Examples
Basic Store Structure
Advanced Usage Patterns
Testing Zustand Stores
Troubleshooting
Related Documents
State Management Overview
Overview of our complete state management approach including client, server, and component state.
Server State Management
Companion documentation for handling server-specific state with TanStack Query.
Integration Patterns
Patterns for integrating server and client state effectively.