mobile-state-management-patterns
Evaluate and choose the optimal state management pattern for mobile application complexity.
Install
mkdir -p .claude/skills/mobile-state-management-patterns && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15220" && unzip -o skill.zip -d .claude/skills/mobile-state-management-patterns && rm skill.zipInstalls to .claude/skills/mobile-state-management-patterns
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Comparison of mobile state management patterns (MVVM, MVI, TEA, Redux, BLoC, Observable, Compose state, SwiftUI state) and how to choose. Use when designing state flow or evaluating a state library.Key capabilities
- →Classify state into UI-ephemeral, Screen, Feature, App, or Persisted tiers
- →Implement unidirectional data flow: Event -> reducer/update -> new State -> View renders
- →Isolate side effects from reducers or update functions
- →Separate transient state from persisted state
- →Ensure a single source of truth for each data fact
- →Test view models independently of the view
How it works
The skill guides the classification of state into tiers and advocates for a unidirectional data flow where events trigger state updates, which then render the view.
Inputs & outputs
When to use mobile-state-management-patterns
- →Choosing a state management library
- →Architecting mobile data flows
- →Evaluating state architecture complexity
About this skill
Mobile State Management Patterns
Instructions
State management in mobile is the discipline of turning user and system events into UI, predictably. The right pattern depends on state complexity, team size, and framework idioms. This skill names the patterns and shows how they map across stacks.
1. Pattern Families
| Family | Shape | Good fit |
|---|---|---|
| MVVM | ViewModel exposes observable state | Most teams, all stacks |
| MVI / Unidirectional | State = reduce(state, intent) | Complex flows, testability |
| TEA (Elm-like) | Model, Msg, update, view | Compose MP, Bloc, Redux Toolkit |
| Redux / Flux | Single store, actions, reducers | Cross-platform with many contributors |
| Observable / Reactive | Streams of state | RN with RxJS/MobX, Combine, Flow |
| Local / ambient | Setters in the view | Very small features only |
2. State Scope Tiers
Classify state before choosing a library:
- UI-ephemeral -- hover, pressed, scroll position. Lives in the view.
- Screen -- loading flag, form draft. Lives in the view model.
- Feature -- a multi-screen flow (checkout). Shared view model or feature store.
- App -- user session, theme, connectivity. Global store.
- Persisted -- survives process death. Backed by disk/keychain.
Using a global store for UI-ephemeral state is the most common smell; so is using local state for app-wide concerns.
3. The Unidirectional Core
Regardless of library, prefer one-way flow:
Event (intent) -> reducer/update -> new State -> View renders
This makes time-travel debug, tests, and undo possible. Two-way bindings are an ergonomic shortcut for small forms, not an architectural default.
4. Cross-Platform Parallels
Counter with loading and error, the simplest non-trivial state:
// Swift (Observation / MV)
@Observable final class CounterVM {
private(set) var count = 0
private(set) var isLoading = false
var error: String?
func increment() async {
isLoading = true; defer { isLoading = false }
do { count = try await api.increment(count) } catch { self.error = "\(error)" }
}
}
// Kotlin (Compose + StateFlow)
class CounterVM(private val api: Api) : ViewModel() {
data class State(val count: Int = 0, val loading: Boolean = false, val error: String? = null)
private val _state = MutableStateFlow(State())
val state: StateFlow<State> = _state
fun increment() = viewModelScope.launch {
_state.update { it.copy(loading = true, error = null) }
runCatching { api.increment(_state.value.count) }
.onSuccess { n -> _state.update { it.copy(count = n, loading = false) } }
.onFailure { e -> _state.update { it.copy(loading = false, error = e.message) } }
}
}
// Dart (Riverpod Notifier)
class CounterState {
const CounterState({this.count = 0, this.loading = false, this.error});
final int count; final bool loading; final String? error;
CounterState copyWith({int? count, bool? loading, String? error}) =>
CounterState(count: count ?? this.count, loading: loading ?? this.loading, error: error);
}
class CounterVM extends Notifier<CounterState> {
@override CounterState build() => const CounterState();
Future<void> increment() async {
state = state.copyWith(loading: true, error: null);
try { state = state.copyWith(count: await ref.read(apiProvider).increment(state.count), loading: false); }
catch (e) { state = state.copyWith(loading: false, error: e.toString()); }
}
}
// TypeScript (Zustand)
type CounterState = { count: number; loading: boolean; error?: string; increment: () => Promise<void> };
export const useCounter = create<CounterState>((set, get) => ({
count: 0, loading: false,
increment: async () => {
set({ loading: true, error: undefined });
try { const n = await api.increment(get().count); set({ count: n, loading: false }); }
catch (e: any) { set({ loading: false, error: String(e) }); }
},
}));
5. Choosing a Library
- Small app, single developer: platform-native (SwiftUI
@Observable, Composeremember/rememberSaveable, FlutterValueNotifier, RNuseState). - Medium app, testability priority: MVVM with observables (StateFlow, Riverpod, Combine, Zustand).
- Complex flows, multi-developer: MVI / TEA / Redux Toolkit / BLoC.
- Cross-platform shared logic (KMP): Coroutines + Flow with reducers in Kotlin, consumed by SwiftUI and Compose.
- RN with React team muscle: Zustand or Redux Toolkit; avoid context for anything that changes often.
6. Anti-Patterns
- Multiple sources of truth for the same fact (e.g., auth token in store and in a singleton).
- Reducers that do network I/O. Side effects belong in commands/middleware/effects.
- Storing derived data. Compute on read; cache with memoization only if profiled.
- Sharing
BuildContext/UIViewController/Contextacross state layers. - Global stores populated eagerly at app start. Hydrate lazily.
7. State Persistence
Separate transient state from persisted state. Good boundaries:
SavedStateHandleon Android restores small UI state across process death.@SceneStorage/@AppStorageon iOS.shared_preferences/ secure storage on Flutter for small values.- AsyncStorage / MMKV on RN for small values.
Large or sensitive state belongs in a database or keychain, not in user preferences.
8. Testing State
- Pure reducers/update functions should be 100% covered by unit tests.
- View models should be tested against fakes of their dependencies.
- UI tests assert on observable state, not on widget internals.
- Use time control (virtual clock,
TestScheduler,fakeAsync) instead of real delays.
Checklist
- Every stateful feature classifies its state scope explicitly.
- Data flow is unidirectional (event -> state -> view) unless a small local form justifies two-way.
- Side effects are isolated from reducers/update functions.
- Persisted state is separated from transient state.
- There is a single source of truth per fact.
- View models are testable without the view.
- Library choice matches team size, app complexity, and stack idioms.
When not to use it
- →Using a global store for UI-ephemeral state
- →Using local state for app-wide concerns
- →Reducers that perform network I/O
Limitations
- →Two-way bindings are not recommended as an architectural default
- →Storing derived data is not recommended without memoization if profiled
- →Global stores should not be populated eagerly at app start
How it compares
This approach structures state management with explicit tiers and a one-way flow, unlike ad-hoc state handling that can lead to unpredictable UI behavior.
Compared to similar skills
mobile-state-management-patterns side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| mobile-state-management-patterns (this skill) | 0 | 4mo | No flags | Intermediate |
| mobile-developer | 5 | 4mo | No flags | Advanced |
| mobile_engineer | 0 | 2mo | No flags | Advanced |
| mobile-architect-agent | 0 | 1mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by almasumdev
View all by almasumdev →You might also like
mobile-developer
sickn33
Develop React Native, Flutter, or native mobile apps with modern architecture patterns. Masters cross-platform development, native integrations, offline sync, and app store optimization. Use PROACTIVELY for mobile features, cross-platform code, or app optimization.
mobile_engineer
Sorravit
Act as a Senior Mobile Engineer. Use when user asks about React Native, Flutter, iOS, Android development, or mobile architecture.
mobile-architect-agent
srednoff888-art
Agent profile for coordinate mobile app architecture across iOS, Android, Expo/React Native, offline states, permissions, and releases. Use when Codex needs a specialist agent perspective for planning, implementation, review, debugging, validation, or handoff in this domain.
react-native-architecture
wshobson
Build production React Native apps with Expo, navigation, native modules, offline sync, and cross-platform patterns. Use when developing mobile apps, implementing native integrations, or architecting React Native projects.
react-native-architecture
Anhvu1107
ALWAYS use this when the request matches React Native Architecture: Production-ready patterns for React Native development with Expo, including navigation, state management, native modules, and offline-first architecture.
mvvm
RitualBoat
Enforce MVVM architecture in React Native/Expo apps, especially screens, hooks as ViewModels, services, and code reviews.