A comparison of four declarative, component-based UI frameworks — the original React (the progenitor) and three reactor-style frameworks sharing the same conceptual lineage: declarative UI with reconciliation.
The goal is not to clone React, but to understand where the state of the art is heading and what ideas can benefit windows-reactor in a Rust/WinUI context.
| React | windows-reactor | Microsoft.UI.Reactor | MauiReactor | |
|---|---|---|---|---|
| Repo | facebook/react | microsoft/windows-rs | microsoft/microsoft-ui-reactor | adospace/reactorui-maui |
| Language | JavaScript / TypeScript | Rust | C# | C# |
| UI backend | Browser DOM / React Native | WinUI 3 (via WinRT/COM) | WinUI 3 (direct) | .NET MAUI (cross-platform) |
| Platform | Web, iOS, Android, desktop | Windows-only | Windows-only | iOS, Android, Windows, macOS |
| Status | Stable (v19, Dec 2024) | Initial (v0.0.0, May 2026) | Experimental (April 2026) | Stable (NuGet, multi-year) |
| License | MIT | MIT / Apache-2.0 | MIT | MIT |
| Aspect | React 19 | windows-reactor | Microsoft.UI.Reactor | MauiReactor |
|---|---|---|---|---|
| Pattern | Functional components with hooks; concurrent rendering | Functional MVU with hooks | Functional MVU with hooks | MVU with class-based state |
| Component model | Functions returning JSX (or class components, legacy) | Trait-based (Component<P>) — functions or structs |
Class-based (Component inherits base) |
Class-based (Component<TState> or Component<TState, TProps>) |
| State management | useState, useReducer, useContext, useMemo, useCallback, useRef, useEffect, useTransition, useDeferredValue, useOptimistic, useActionState |
use_state, use_reducer, use_reducer_fn, use_async_state, use_memo, use_ref, use_effect, use_callback, use_context |
UseState, UseReducer, UseEffect, UseMemo, UseRef, UseObservable, UseCollection |
SetState(s => ...) mutating a POCO state object; no hooks |
| Reconciler | Fiber-based concurrent reconciler with interruptible rendering, automatic batching, priority lanes | Custom keyed-diffing, element pooling, skip-unchanged, render coalescing | Virtual element tree, keyed diffing, element pooling, render coalescing, skip-unchanged | Visual tree diffing, mounting/unmounting lifecycle |
| Props | Plain JS objects / destructuring | Generic P: Clone + PartialEq on component trait |
Constructor parameters / fluent modifiers | [Prop] attribute with source-gen fluent methods, or Props class for navigation |
| Element tree | Virtual DOM (React Elements → Fiber nodes) | Enum-based (Element sum type with widget variants) |
Immutable element descriptions (virtual DOM) | VisualNode class hierarchy |
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Count: {count}</h1>
<div style={{ display: 'flex', gap: 8 }}>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
</div>
);
}- JSX markup compiled to
React.createElementcalls - Components are plain functions returning JSX
- Hooks (
useState,useEffect, etc.) called at top level - Props are destructured JS objects — no special trait or annotation
- Children via JSX nesting; lists via
.map()withkeyprop - React Compiler eliminates manual
useMemo/useCallback/React.memo
fn render(&self, _: &(), cx: &mut RenderCx) -> Element {
let (count, set_count) = cx.use_state(0);
vstack((
text_block(format!("Count: {count}")),
hstack((
button("-").on_click({
let s = set_count.clone();
move || s.call(count - 1)
}),
button("+").on_click(move || set_count.call(count + 1)),
)).spacing(8.0),
)).into()
}- Factory functions:
button(label),text_block(text),vstack(children),hstack(children) - Fluent modifiers:
.margin(...),.background(...),.opacity(...),.with_key(...) - Tuple/array-based children:
vstack((a, b, c))orvstack([a, b, c]) - No macros required; pure Rust expressions
class MyApp : Component {
public override Element Render() {
var (count, setCount) = UseState(0);
return VStack(
Heading($"Count: {count}"),
HStack(8,
Button("-", () => setCount(count - 1)),
Button("+", () => setCount(count + 1))
)
);
}
}- Static factory methods via
using static Microsoft.UI.Reactor.Factories - Fluent modifier chains (typed)
- Hooks are methods on the
Componentbase class - No XAML, no code-behind, no ViewModels
class CounterPageState { public int Counter { get; set; } }
class CounterPage : Component<CounterPageState> {
public override VisualNode Render()
=> ContentPage("Counter",
VStack(
Label($"Counter: {State.Counter}"),
Button("Click", () => SetState(s => s.Counter++))
).Spacing(10).Center()
);
}- Factory methods matching MAUI control names
- Fluent modifier chains (
.Spacing(),.Center(),.FontSize()) - Class-based state (POCO state class as generic parameter)
- No hooks — state mutation via
SetStatelambda - Platform extensions:
.OnAndroid(...),.OniOS(...),.OnWindows(...)
| Feature | React 19 | windows-reactor | MS.UI.Reactor | MauiReactor |
|---|---|---|---|---|
| Hooks (useState, etc.) | ✅ Full set + new hooks (see below) | ✅ Full set | ✅ Full set + UseObservable | ❌ Class-based state only |
| React Compiler (auto-memo) | ✅ Build-time automatic memoization | ❌ (Rust ownership model makes this less needed) | ❌ | ❌ |
| Concurrent rendering | ✅ Fiber scheduler, interruptible renders, priority lanes | ❌ (single-pass render) | ❌ | ❌ |
| Transitions (useTransition) | ✅ Non-blocking state updates with isPending | ❌ | ❌ | ❌ |
| Deferred values (useDeferredValue) | ✅ Show stale UI while fresh content loads | ❌ | ❌ | ❌ |
| Suspense | ✅ Declarative loading states, nested boundaries | ❌ (Resource enum with view builder) | ❌ | ❌ |
| Optimistic updates (useOptimistic) | ✅ Temporary UI during async Actions | ❌ | ❌ | ❌ |
| Actions (useActionState) | ✅ Async transitions with auto pending/error | ❌ | ❌ | ❌ |
| Error boundaries | ✅ Class-based error boundaries | ✅ | Unknown | ❌ |
| Context/providers | ✅ createContext + useContext + use |
✅ (Context<T>, use_context) |
Likely (React-inspired) | ❌ (uses DI instead) |
| Keyed reconciliation | ✅ key prop |
✅ | ✅ | ✅ (via Key property) |
| Async state | ✅ (use hook, Suspense, Actions) |
✅ (thread-safe AsyncSetState, use_resource, use_mutation) |
Unknown | Partial (Task-based) |
| Server Components | ✅ (RSC — zero client JS for server-rendered subtrees) | N/A (desktop, no server) | N/A | N/A |
| Hot reload | ✅ (Fast Refresh) | ❌ (Rust limitation) | ✅ (MetadataUpdateHandler) |
✅ (custom hot reload console) |
| Flex layout (Yoga) | ✅ (CSS Flexbox native to web) | ❌ | ✅ (full Yoga port) | ❌ (uses MAUI layouts) |
| Charting | ❌ (ecosystem libraries) | ❌ | ✅ (D3 port) | ❌ |
| Data grid | ❌ (ecosystem libraries) | ❌ | ✅ (DataGrid, PropertyGrid) | ❌ (wrap MAUI controls) |
| Navigation | ✅ (React Router, framework routers) | ❌ (single-window) | ✅ (type-safe routing) | ✅ (Shell navigation) |
| Animations | ✅ (CSS transitions, libraries) | ✅ (compositor transitions) | ✅ (keyframes, connected) | ✅ (MAUI animations) |
| Theming | ✅ (Context-based, CSS variables) | ✅ (ThemeRef tokens, dark/light) |
✅ (ThemeRef, per-control) | ✅ (MAUI theming + ThemeKey) |
| Accessibility | ✅ (ARIA attributes, semantic HTML) | ✅ (modifiers) | ✅ (WCAG 2.1 AA, linting) | Partial (MAUI built-in) |
| Templated/virtual lists | ✅ (windowing via react-window/virtuoso) | ✅ (list_view, grid_view, recycling) |
✅ (ListView, ItemsRepeater) | ✅ (CollectionView) |
| CLI tooling | ✅ (create-react-app, Vite, Next.js CLI) | ❌ | ✅ (mur CLI) |
✅ (template pack, hot reload CLI) |
| IDE integration | ✅ (VS Code, WebStorm, React DevTools) | Standard Rust (rust-analyzer) | ✅ (VS Code extension, preview) | Standard .NET tooling |
| Compiler/static analysis | ✅ (React Compiler, ESLint plugin) | N/A | ✅ (Roslyn analyzers) | ❌ |
| Localization | ❌ (ecosystem: react-intl, i18next) | ❌ | ✅ (ICU, source gen) | ❌ (use MAUI localization) |
| Commanding | ❌ | ❌ | ✅ (command records, accelerators) | ❌ |
| Docking/multi-window | ❌ | ❌ | ✅ (VS-style docking) | ❌ |
| WinForms interop | ❌ | ❌ | ✅ | ❌ |
| Shell integration | ❌ | ❌ | ✅ (taskbar, tray, thumbnails) | ❌ |
| DevTools/MCP | ✅ (React DevTools, profiler) | Diagnostics feature flag | ✅ (in-process MCP server) | ❌ |
| Cross-platform | ✅ (Web; React Native for mobile) | ❌ (Windows-only) | ❌ (Windows-only) | ✅ (iOS, Android, Win, Mac) |
| AOT support | N/A (JIT-compiled JS) | Native (Rust) | Partial (documented matrix) | ✅ (.NET NativeAOT) |
| Dependency injection | ❌ (Context serves this role) | ❌ | Unknown | ✅ (via Services property) |
- The original — 10+ years of production use, billions of users, enormous ecosystem
- React Compiler — build-time automatic memoization eliminates
useMemo/useCallback/React.memoboilerplate; Meta saw 31-46% faster PR authoring after removing manual memoization overhead - Concurrent rendering — Fiber architecture with interruptible renders, priority lanes, and automatic batching; the UI stays responsive during expensive state transitions
- Transitions & deferred values —
useTransitionanduseDeferredValuelet the framework keep the current UI interactive while preparing new state in the background; no jank during filtering, navigation, or data loads - Suspense — declarative loading boundaries (
<Suspense fallback={...}>) that compose and nest; the framework manages showing/hiding fallbacks automatically - Optimistic updates —
useOptimisticgives instant perceived responsiveness during async mutations, with automatic rollback on failure - Actions —
useActionState+ form actions unify pending state, error handling, and optimistic UI into a single composable pattern useAPI — read promises and context conditionally in render, enabling patterns that were previously impossible with hooks rules- Server Components — zero-JS server-rendered subtrees that reduce bundle size and enable server-side data fetching (web-specific, not directly applicable to desktop)
- Rust ownership model — zero-cost abstractions, no GC, no runtime overhead
- Minimal dependencies — builds on
windows-rsecosystem crates only - Backend abstraction —
Backendtrait enables headless testing viaRecordingBackend - Extremely lean — ~105K lines total (incl. generated bindings); no CLI, no hot reload, no IDE plugin
- Build-time code generation —
build.rsgenerates WinUI bindings at compile time - Designed for embedding — can integrate into existing windows-rs applications
- Most feature-complete — charting, data grids, docking, shell integration, commanding, localization, flex layout
- AI-first development — ships agent plugins, MCP server, SKILL.md for AI assistants
- Full toolchain —
murCLI, VS Code extension, Roslyn analyzers, project templates - Directly backed by the WinUI team — features designed to flow back into WinUI itself
- Hot reload + live preview — iterate without restarting
- Extensive testing — 2,200+ unit tests, Appium e2e, stress/perf benchmarks
- Cross-platform — single codebase for iOS, Android, Windows, macOS
- Production-ready — stable NuGet, years of community usage, documentation, videos
- Familiar to .NET developers — standard POCO state, no hooks to learn
- Source generators —
[Scaffold]auto-generates wrapper classes for native MAUI controls - Hot reload — works across all platforms including mobile emulators
- Community ecosystem — sample apps, third-party controls, active GitHub community
React is the progenitor of the component/hooks model that windows-reactor adopts. Rather than cloning React features verbatim, the following analyzes which React 19 advances have analogues or inspiration value for a Rust/WinUI desktop framework.
What React does: The React Compiler is a build-time Babel plugin that automatically inserts useMemo, useCallback, and React.memo calls. At Meta, only ~8% of PRs used manual memoization, and those PRs took 31-46% longer to author. The compiler eliminates this cognitive overhead entirely.
Relevance to windows-reactor: Rust's ownership model already provides some of this benefit — Clone + PartialEq props naturally enable skip-unchanged diffing, and there is no equivalent of JavaScript's referential-identity footgun where {} !== {}. However, windows-reactor could benefit from:
- Compile-time diffing hints — a proc-macro or
build.rspass that analyzes component render functions and auto-derives smarter diffing (e.g., field-level partial comparison rather than fullPartialEq) - Memoization of expensive element subtrees —
use_memoexists but could be more ergonomic; consider whether the reconciler itself can detect stable subtrees without explicit memoization - Lint/clippy rules enforcing "rules of hooks" at compile time, analogous to React Compiler's ESLint plugin
What React does: React's Fiber architecture enables interruptible rendering — a large re-render can be paused mid-tree to handle a higher-priority update (e.g., user typing), then resumed. useTransition marks state updates as non-urgent, keeping the current UI interactive while React prepares the new state in the background. useDeferredValue lets a value "lag behind" so that expensive subtrees render with stale data while urgent updates (like input) remain responsive.
Relevance to windows-reactor: This is one of React's most architecturally significant advances. The current reconciler does a single-pass synchronous diff+patch. For most desktop UIs this is fine, but for complex scenarios (large lists, real-time data, multi-pane layouts) it could cause frame drops. Potential approaches:
use_transitionhook — mark expensive state updates as "low priority," allowing the framework to keep the UI responsive (showis_pendingflag) while computing the new tree. The reconciler would need to support yielding mid-diff or splitting work across frames.use_deferred_valuehook — return the previous value during expensive re-renders. Simpler to implement than full concurrent rendering — doesn't require an interruptible reconciler, just a "double-buffer" of state with intelligent scheduling.- Render coalescing already exists; the next step would be render prioritization — urgent updates (input, scrolling) preempt batch updates (data loads, tab switches).
What React does: <Suspense fallback={<Loading />}> wraps any subtree that may need to load data or code. When a child suspends (throws a Promise), React shows the fallback automatically. Suspense boundaries compose and nest — each boundary controls its own loading state independently. Combined with transitions, Suspense avoids jarring "flash to spinner" by keeping stale content visible during navigation.
Relevance to windows-reactor: The Resource<T> enum with view() builder pattern already fills part of this role — it provides Loading, Ready, Reloading, and Error states. However, it is per-resource, not per-subtree. A Suspense-like boundary would enable:
- Coordinated loading — wrap multiple
use_resourcecalls in a single boundary so they all appear together (React's "reveal content together" pattern) - Nested loading sequences — outer skeleton → inner content, without manually threading loading state through every component
- Cleaner component APIs — components wouldn't need to return
ResourceView; they could simply callcx.use_resource(...)and let a parent Suspense boundary handle the loading UI - Implementation: a
suspense(fallback, children)element where child components can "suspend" by returning a sentinel, causing the reconciler to render the fallback subtree until all children are ready
What React does: useOptimistic shows a temporary state during an async Action. If the Action succeeds, the real state replaces the optimistic value seamlessly. If it fails, React automatically reverts to the pre-optimistic state. This makes mutation-heavy UIs (like/unlike, add to cart, inline editing) feel instant.
Relevance to windows-reactor: use_async_state and use_mutation provide the async write path, but the developer must manually manage the "show optimistic value, then replace with real value, then handle rollback" pattern. A dedicated use_optimistic hook would:
let (optimistic_name, set_optimistic) = cx.use_optimistic(current_name);
// Inside an async action:
set_optimistic("New Name".to_string());
let result = save_name("New Name").await;
// On success: real state updates, optimistic value converges
// On failure: automatic rollback to current_nameThis composes naturally with use_async_state and could be built as a higher-level hook on top of existing primitives.
What React does: React 19's useActionState unifies three concerns that previously required manual wiring:
- Pending state —
isPendingis true while the action runs - Error state — the action returns error data that the component renders
- Sequential ordering — later actions supersede earlier ones automatically
Combined with <form action={fn}>, this creates a declarative mutation model where the framework handles the entire submit → pending → success/error lifecycle.
Relevance to windows-reactor: Desktop forms (settings pages, data entry, dialogs) have the same pending/error/success lifecycle. A use_action hook could combine use_async_state + use_reducer into a single pattern:
let (error, submit, is_pending) = cx.use_action(|_prev, form_data: FormData| async move {
match save_settings(form_data).await {
Ok(_) => None, // no error
Err(e) => Some(e.to_string()),
}
});This is less urgent than transitions or Suspense, but would reduce boilerplate for form-heavy apps.
What React does: The use API lets you read a Promise or Context conditionally in render — after early returns, inside loops, or in branches. Unlike hooks (which must be called unconditionally at the top level), use can be called anywhere. This unlocks patterns like "if no children, return null; otherwise read theme context."
Relevance to windows-reactor: Rust's hooks must currently be called unconditionally and in consistent order (same as React's traditional hooks rules). A use-like API is harder in Rust because there's no equivalent of throwing a Promise. However:
- For context,
cx.use_context(ctx)could be made safe to call conditionally if context reads are implemented as lookups rather than positional hooks - For resources, a
cx.use(future)that integrates with a Suspense-like boundary could replace the currentuse_resourcewhen the component wants to "just get the data or suspend trying" - This is a stretch goal — the current hooks model works well, and conditional hooks are a convenience, not a necessity
What React does: Beyond automatic memoization, the React Compiler includes an ESLint plugin that statically identifies "Rules of React" violations — stale closures, hook ordering bugs, mutation of props during render. Meta plans to eventually require the compiler for new React features.
Relevance to windows-reactor: Rust's type system already prevents many classes of bugs that the React Compiler catches (use-after-move, data races, mutation of borrowed data). However, a #[clippy::reactor] lint group could catch reactor-specific issues:
- Hooks called conditionally or inside loops
SetStatecaptured but never called (likely dead code)use_effectwith empty deps that references changing state (stale closure equivalent in Rust closures)- Components that clone large props unnecessarily
Some React 19 features are not relevant to windows-reactor:
- Server Components / Server Actions — web-specific; desktop apps don't have a server/client split
<form>Actions with automatic reset — HTML-form-specific; WinUI has different form controls- Streaming SSR / Selective Hydration — server rendering concepts
- React Native bridge — mobile cross-platform, not applicable to WinUI-only
| Gap | Impact | Notes |
|---|---|---|
| No concurrent/interruptible rendering | Frame drops possible with complex UIs | React's Fiber scheduler is the gold standard; could start with use_transition |
| No Suspense boundaries | Per-resource loading only, not per-subtree | Resource<T>.view() exists; Suspense would compose loading across components |
| No transitions/deferred values | All state updates are equally urgent | use_transition + use_deferred_value would improve perceived responsiveness |
| No optimistic updates | Manual rollback logic for async mutations | use_optimistic could be a thin layer over use_async_state |
| No hot reload | Slower iteration cycle | Inherent Rust limitation; mitigated by fast compile times and headless tests |
| No navigation/routing | Single-window only | Could be added; the Window abstraction exists |
| No CLI/templates | Manual project setup | Could ship a cargo-generate template |
| No flex layout | Limited layout options | Has StackPanel, Grid, Canvas, RelativePanel — covers most cases |
| No charting/data grid | Enterprise gap | Focused on core framework first |
| No commanding | Manual accelerator wiring | Keyboard accelerators exist but not unified command model |
| No localization framework | Manual i18n | Standard Rust i18n crates can fill this |
| No cross-platform | Windows-only | By design — uses WinUI directly for maximum fidelity |
| No AI/agent integration | No MCP/plugin story | Diagnostic hooks exist; could be extended |
| No static analysis / lint rules | Reactor-specific bugs not caught at compile time | Rust's type system covers most, but hooks-order lints would help |
All four frameworks share:
- Virtual element trees diffed against previous renders to minimize UI mutations
- Factory-function DSL replacing markup (XAML/XML/HTML) with typed code (React uses JSX which compiles to function calls)
- Fluent modifier chains for styling and layout (React uses props/style objects instead of method chains, but the concept is the same)
- Keyed reconciliation for efficient list updates
- Component composition as the primary abstraction
- Unidirectional data flow — state flows down, events flow up (React: "lifting state up"; reactor: props + callbacks)
- Hooks as the primary state/effect mechanism (React, windows-reactor, MS.UI.Reactor; MauiReactor diverges with class-based state)
The programming model is closest between windows-reactor and React — both use functional components with hooks (use_state/useState, use_effect/useEffect, etc.). Microsoft.UI.Reactor is the C# equivalent. MauiReactor diverges most with class-based state and cross-platform targeting.
React 19's newest innovations — concurrent rendering, transitions, Suspense, optimistic updates, and the compiler — represent the frontier. These ideas are proven at Meta's scale (Facebook, Instagram, Threads) and are the direction the ecosystem is moving. windows-reactor should selectively adopt the patterns that make sense for desktop Rust, particularly transitions, Suspense boundaries, and optimistic updates.
| If you want… | Use… |
|---|---|
| Web/mobile, massive ecosystem, cutting-edge concurrent rendering | React |
| Maximum performance, Rust, minimal footprint, Windows-only | windows-reactor |
| Full-featured declarative WinUI with C#, AI-assisted dev, enterprise features | Microsoft.UI.Reactor |
| Cross-platform mobile+desktop with declarative C# | MauiReactor |
windows-reactor occupies a unique niche: it brings the React/hooks programming model to Rust developers targeting Windows, with the safety and performance characteristics of Rust. It is the leanest of the four and the most suitable for embedding into existing Rust/Windows applications or system-level tools that need a UI layer without a managed runtime.
Based on the analysis above, these are the highest-value React 19 features to adapt for windows-reactor, in rough priority order:
use_transition+use_deferred_value— relatively low implementation cost, high UX impact for responsive UIs during expensive state changes- Suspense boundaries — composable loading states across component trees, building on the existing
Resource<T>primitives use_optimistic— thin convenience hook over existing async state for instant perceived responsivenessuse_action(action state) — unified pending/error/success pattern for form-like workflows- Hooks-order linting — clippy lint or proc-macro to catch common reactor-specific mistakes
- Interruptible reconciler — the most ambitious item; would require fundamental changes to the render pipeline, but would enable true concurrent rendering