Status
Product name is MVVMExpress (MVVM + Express). Package prefix is Plugin.Maui.MVVMExpress. Core targets net10.0 and does not reference Microsoft.Maui.Controls. Manual INotifyPropertyChanged and hand-written commands are first-class; generators are an accelerator, not a requirement.
License
MVVMExpress is MIT licensed. There is no community-versus-commercial split and no revenue threshold. You may use the packages in commercial apps without a paid framework license. The SPDX identifier is MIT; LICENSE lives at the repository root and is packed with each nupkg.
That is a product choice, not a jab at other frameworks. If you already pay for a commercial MVVM stack, keep it. MVVMExpress exists so a MAUI team can take ViewModels, async state, and Shell or page navigation without a second license conversation.
Why this exists
A production MAUI app needs more than INotifyPropertyChanged and ICommand: ViewModel lifecycle bound to page lifetime, async work with cancellation, timeout, retry, and busy state, strongly typed navigation, UI state richer than a boolean IsBusy, and testable ViewModels with no static MAUI calls.
- CommunityToolkit.Mvvm covers properties, commands, and messaging.
- Prism.Maui covers page navigation and dialogs. It does not support Shell.
- ReactiveUI covers observable pipelines and activation, and requires System.Reactive.
None of those, alone, is a MAUI-first operation + state + scope framework. Combining all three creates package, namespace, and mental-model collisions. MVVMExpress is that shell. It is not a fork of those libraries. Capability work — captive portal, HTTP cache, offline sync, form XAML, flags, deep links — stays in focused MauiEssentials plugins.
Design principles
- Core is UI-framework-free. Plugin.Maui.MVVMExpress.Core must not reference Microsoft.Maui.Controls.
- Optional means optional. Navigation, dialogs, validation, reactive, pagination, and generators are separate packages.
- Interfaces at the ViewModel boundary. ViewModels depend on INavigator, IDialogs, IMainThread, IConnectivityProbe — never on Shell, Page.DisplayAlert, or MainThread statics.
- Async-first, cancellation-first. Every public async API accepts CancellationToken.
- No hidden global state. No static service locator in Core.
- Compose MauiEssentials. Connectivity, cache, offline, permissions, flags, deep links, and secure session are adapter surfaces, not new engines.
- Source generators are an accelerator, not a requirement.
- AOT and trimming are default constraints. Reflection-based registration is a debug fallback.
- Do not silently swallow exceptions. Every catch transforms to Outcome, calls IErrorSink, logs and rethrows, or maps OperationCanceledException to Cancelled.
- One window is not the app. Navigation, dialogs, and scopes are keyed by window context, not Application.Current.MainPage.
How to read these docs
The left nav follows the surfaces a production MAUI app actually touches. Start here for the contract. Getting started is the install-and-wire path, including the first screen and Playground clone. Comparison evaluates CommunityToolkit.Mvvm, Prism.Maui, and ReactiveUI and includes the syntax map. Application model covers ViewModels, commands, DI, messaging, and Reactive. Application shell covers navigation, chat host, dialogs, validation, forms, and lists. Composition and internals explain packages, adapters, platforms, the operation pipeline, tests, and scale. Release covers generators and the shipped roadmap.
- Shipped in 1.0.0 means types exist and tests exist. Phases 1–7 plus UseAuth, the host-safe navigator, NavigationPage replace-root, and chat-host APIs are complete.
- 1.0.0 is the SemVer lock. Public 1.x APIs stay source-compatible; breaking changes wait for 2.0.0. Known limitations are accepted 1.0 scope, not remaining product work. Next work is Phase 8 (1.1.0).
- Type names stay unique so CommunityToolkit.Mvvm or Prism can sit in the same app if you need them.
Naming — no collisions
Core type names must not collide with CommunityToolkit.Mvvm or Prism when both are referenced.
| Concept | MVVMExpress | Avoid copying |
|---|---|---|
| Observable base | ObservableModel | ObservableObject |
| ViewModel base | ViewModel / PageViewModel | Prism BindableBase |
| Sync command | ModelCommand | RelayCommand, DelegateCommand |
| Async command | AsyncModelCommand | AsyncRelayCommand, ReactiveCommand |
| Notify attribute | [Notify] / [NotifyAlso] | [ObservableProperty] |
| Messenger | IMessageHub | IMessenger, IEventAggregator |
| Navigation | INavigator | INavigationService |
| Dialogs | IDialogs | IDialogService |
| Parameters | typed records / IAcceptNavArgs<T> / IAcceptNavQuery | INavigationParameters |
| Result | Outcome / Outcome<T> | competing Result<T> packages |
The Comparison page has the side-by-side syntax map ([Notify] versus [ObservableProperty], INavigator versus INavigationService). The attribute is [ModelCommand] / [AsyncModelCommand], not [Command].
Feature comparison
Designed product surface, validated 2026-09-01 against CommunityToolkit.Mvvm 8.4, Prism.Maui 9, and ReactiveUI. This table does not claim MVVMExpress is faster than the others. Shipping versus designed is tracked in the repository FEATURE-MATRIX.md.
| Feature | MVVMExpress | CommunityToolkit.Mvvm | Prism.Maui | ReactiveUI |
|---|---|---|---|---|
| Observable properties | Yes | Yes | Yes | Yes |
| Commands / async commands | Yes | Yes | Yes / Partial | Yes |
| Source generators | Yes ([Notify], commands, register, routes) | Yes | No | Yes |
| Navigation (Shell or page) | Yes (Shell + page) | No | Yes (page only) | Yes |
| Lifecycle + cancellation | Yes | No | Yes | Yes |
| Dialogs / toast | Yes | Separate | Yes | Extensions |
| Validation | Yes | Yes | Extensions | Yes |
| Reactive derived state | Yes (CombineLatest; Rx optional) | No | No | Yes (Rx required) |
| Pagination + refresh + search | Yes | No | Extensions | Extensions |
| Offline / cache abstractions | Yes (adapters) | No | No | Extensions |
| Unified AsyncState<T> | Yes | No | No | Extensions |
| Typed navigation record args | Yes | No | No (dictionary / URI) | Partial |
| Memory-leak GC tests | Yes (VM, command, Button pop, messenger) | Partial | Partial | Partial |
| Small / mid / large list batching | Yes (AddRange) | App code | App code | App / Rx |
| Testing package | Yes | Partial | Yes | Yes |