MVU in Other Ecosystems
MVU in Other Ecosystems
The Elm Architecture (Model–View–Update) outgrew its original language and now lives across nearly every major ecosystem under various names. This note catalogues the major implementations so you can spot the pattern wherever you encounter it.
The shape is always the same: a Model representing state, a Msg/Action/Event type representing what happened, a pure Update function that produces new state, a View function that renders state, and an explicit way to handle side effects (Cmd, Effect, Task, Thunk).
Bubbletea (Go)
Bubbletea by Charm. The most popular TUI framework in Go. Explicit Elm Architecture.
type Model struct {
count int
}
type tickMsg time.Time
func (m Model) Init() tea.Cmd { return nil }
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "+": m.count++
case "-": m.count--
case "q": return m, tea.Quit
}
case tickMsg:
m.count++
}
return m, nil
}
func (m Model) View() string {
return fmt.Sprintf("count: %d", m.count)
}
func main() {
p := tea.NewProgram(Model{})
p.Run()
}
Init returns the initial Cmd. Update is the reducer. View renders. tea.Cmd is the side-effect type. Identical to Elm in shape.
Used by gh (the GitHub CLI's TUI mode), glow, soft-serve, dozens of small CLIs.
tui-realm (Rust)
tui-realm by Christian Visintin. TEA on top of ratatui (formerly tui-rs). More structured than rolling your own.
Key concepts: Components (each with its own Model/Update/View), subscriptions for cross-component messages, properties for parent-to-child config.
A bit heavier than Bubbletea. Trade-off: more structure for free, less control. For lazydap, Lazydap chose plain ratatui + hand-rolled Elm Architecture per Lazydap D012 — wanted to learn the pattern by writing it.
Iocraft (Rust)
Iocraft. React-flavoured Rust TUI: components defined via element! macro, hooks (use_state, use_effect), declarative composition.
Closer to React than to Elm-strict, but fundamentally the same shape under the hood.
Compose Multiplatform (Kotlin)
JetBrains' Compose, originally for Android, now multiplatform (desktop, iOS, web). Declarative UI framework with similar shape:
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
mutableStateOf is React's useState-equivalent. @Composable functions are pure renders of state. Effects via LaunchedEffect. The pattern's Elm-flavoured even though Kotlin syntax doesn't make it explicit.
For more architectural rigor, Compose users adopt MVI (Model-View-Intent) — explicit reducers and intents, identical to Elm's Update/Msg.
SwiftUI (Apple)
struct Counter: View {
@State var count = 0
var body: some View {
Button("Count: \(count)") { count += 1 }
}
}
@State is React's useState. body is the view as a function of state. SwiftUI is reactive in the Elm sense — state changes trigger view recomputation. Less explicit reducers in idiomatic SwiftUI, but the pattern is there. The Composable Architecture (TCA) by Point-Free pushes SwiftUI toward strict Elm/MVU.
Flutter (Dart)
Flutter's setState model is React-class-style. The community-built BLoC (Business Logic Component) and Riverpod patterns push Flutter toward Elm-shaped reducers. Redux for Flutter ports Redux directly.
Idiomatic Flutter is more imperative than Elm; the patterns exist for those who want them.
.NET — Elmish, F#'s Fable
Elmish is the Elm Architecture for F#, originally targeting Fable (F#-to-JS). Direct port:
type Model = { Count: int }
type Msg =
| Increment
| Decrement
let init () = { Count = 0 }, Cmd.none
let update msg model =
match msg with
| Increment -> { model with Count = model.Count + 1 }, Cmd.none
| Decrement -> { model with Count = model.Count - 1 }, Cmd.none
let view model dispatch =
div [] [
button [ OnClick (fun _ -> dispatch Decrement) ] [ str "-" ]
str (string model.Count)
button [ OnClick (fun _ -> dispatch Increment) ] [ str "+" ]
]
Used in Fable React apps, Avalonia desktop apps, MAUI mobile. F# is one of the few mainstream languages where Elmish feels native (functional first, sum types, pattern matching).
Vue (JavaScript)
Vue 3's Composition API is reactive but not strictly TEA-shaped. Vuex (the official state management library) has reducers; Pinia (its successor) is simpler.
The community is moving toward more reducer-based patterns; "stores" in Vue 3 + Pinia look like Redux slices.
Other notable cases
- Angular + NgRx — Redux in Angular's idioms.
- Svelte — uses reactive statements; explicit Elm pattern via
svelte-storeor stores. - Solid.js — reactive primitives; similar in spirit.
- HTMX-style apps — server returns full HTML on every interaction; the entire page is the "view," server is the reducer. Different mechanism, same conceptual shape.
- Elixir Phoenix LiveView — server-side reducer, client-side rendering, transparent state sync. Very Elm-y.
What this teaches
The pattern doesn't depend on the language. It doesn't depend on syntax. It doesn't depend on a framework — you can hand-roll it (Lazydap does, in 50 lines of Rust). What matters is the discipline:
- All state mutation in one place
- View is a pure function of state
- Side effects are explicit and isolated
Wherever those three hold, you have an Elm-flavoured app. The language and framework are details.
Why this is worth knowing
If you're picking a framework for a new reactive app, the choice is less about TEA-vs-not (almost everything modern is TEA-flavoured) and more about:
- Language preference
- Performance characteristics
- Ecosystem and tooling
- Team familiarity
Don't expect "but it doesn't have proper TEA" to be a real differentiator in 2026. The frameworks have all converged. The discipline is portable.
See also
- The Elm Architecture — the canonical pattern
- Elm (the language) — where it came from
- Reducers — the generalised primitive
- React's Evolution Toward Elm — the JS lineage
- Lazydap — hand-rolled Elm in Rust
- Bubbletea: https://github.com/charmbracelet/bubbletea
- tui-realm: https://github.com/veeso/tui-realm
- Elmish: https://elmish.github.io/elmish/
- The Composable Architecture (Swift): https://github.com/pointfreeco/swift-composable-architecture