FlowMVI usage guidance. Use when working with FlowMVI stores/containers, plugin pipelines, composing stores, decorators, or authoring plugins.
/plugin marketplace add respawn-app/FlowMVI/plugin install respawn-app-flowmvi@respawn-app/FlowMVIThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/api-signatures.mdreferences/docs-index.mdreferences/plugin-callbacks.mdreferences/plugin-signatures.mdUse this to learn the API of FlowMVI. Prefer the bundled references for exact APIs and the live official docs for plugins/integrations/state.
Key references in this skill:
references/api-signatures.md for core API signatures.references/plugin-signatures.md for all plugin/decorator signatures.references/plugin-callbacks.md for callback behavior and decorator semantics.references/docs-index.md for live docs URLs (plugins, integrations, state).Use rg over references/*signatures*.md for discovery and open URLs from references/docs-index.md when you need full docs.
MVIState, MVIIntent, MVIAction.data class / data object.EmptyState if no state is needed.Example (state family):
sealed interface CounterState : MVIState {
data object Loading : CounterState
data class Content(val count: Int) : CounterState
data class Error(val cause: Exception) : CounterState
}
MVIIntent: inputs/events. Use sealed interfaces for MVI style.MVIAction: one-off effects, never rely on them for critical logic.Nothing or the no-action store overload.sealed interface CounterIntent : MVIIntent {
data object Tap : CounterIntent
data class Set(val value: Int) : CounterIntent
}
sealed interface CounterAction : MVIAction {
data class ShowToast(val text: String) : CounterAction
}
Prefer a container per screen/feature and expose the store as a property.
class CounterContainer : Container<CounterState, CounterIntent, CounterAction> {
override val store = store(initial = CounterState.Loading) {
configure { debuggable = BuildFlags.debuggable; name = "CounterStore" }
reduce { intent ->
when (intent) {
is CounterIntent.Set -> updateState<_, CounterState.Content> { copy(count = intent.value) }
is CounterIntent.Tap -> action(CounterAction.ShowToast("tap"))
}
}
}
}
Use lazyStore when construction is heavy or should be deferred.
Intents can be lambdas (Orbit MVI-style):
// contract
typealias CounterIntent = LambdaIntent<CounterState, CounterAction>
// sending
fun onTap() = store.intent { updateState<State.Content, _> { copy(count = count + 1) } }
When using LambdaIntent:
reduceLambdas plugin to invoke intent blocks.ImmutableStore/ImmutableContainer at the edges to avoid leaking context.FlowMVI serializes state transactions by default.
Use:
updateState { } for atomic updates.withState { } for safe reads.updateState<T, R> { } and withState<T, R> { } (note always two parameters, of which first is target state, and second is always underscore _). Also these need an import.updateStateImmediate only for performance-critical hot paths; it bypasses plugins and safety.You get PipelineContext receiver inside most of the store DSLs and plugin callbacks. Key operations:
updateState { }, withState { }, updateStateImmediate { }action(action) and intent(intent)config for store config and loggingFlow.consume(context) for safe collection in store contextExact signatures in references/api-signatures.md.
Configure inside store { configure { ... } }. Defaults are defined in StoreConfigurationBuilder.
Key flags:
debuggable: enable validations + verbose logging.name: used for logging/debugging and store identity.parallelIntents: process intents concurrently (state safety becomes critical).coroutineContext: merged with start scope.actionShareBehavior: Distribute, Share, Restrict, Disabled.intentCapacity + onOverflow: buffer sizing and backpressure.stateStrategy: Atomic(reentrant = true/false) or Immediate.allowIdleSubscriptions, allowTransientSubscriptions.logger, verifyPlugins.See exact defaults in references/api-signatures.md and use references/docs-index.md for the latest state docs.
Exact signatures: references/api-signatures.md.
Plugins are the core of FlowMVI. Order matters.
reduce early if you want later plugins to observe processed intents.All plugin signatures are in references/plugin-signatures.md.
All callback signatures and behavior are in references/plugin-callbacks.md.
Open references/docs-index.md and references/plugin-signatures.md for the full list. Core plugins include:
Use the plugin DSL to intercept store events. Use lazyPlugin when you need config at creation time.
val analytics = plugin<State, Intent, Action> {
onIntent { intent -> log { "intent = $intent" }; intent }
}
Rules:
updateState inside onState to avoid loops.onUndeliveredIntent/onUndeliveredAction.Use references/plugin-callbacks.md for full behavior rules.
Decorators wrap plugins and can short-circuit the entire chain.
child.onX(...) in a decorator, the chain stops there.Use decorators only when:
Tie lifecycle of child stores to a parent store:
val child = store(ChildState.Loading) { /* ... */ }
val parent = store(ParentState.Loading) {
this hasChild child
}
Delegate state/actions from another store and project them:
val feedState by delegate(feedStore) { action -> /* handle */ }
whileSubscribed { feedState.collect { state -> /* render */ } }
Use DelegationMode.Immediate when you need always-hot projections.
Default WhileSubscribed mode yields stale projections when no subscribers.
Prefer building tree-like store hierarchies using children and delegates for complex business logic.
Use savedstate module for persistence, metrics module to set up metrics, and essenty for decompose integration.
More info in references/docs-index.md