Architecture
Understand the NatUI reconciler, bridge, native hosts, controlled state, and runtime paths.
┌────────────────────────── JavaScript process ──────────────────────┐
│ React application │
│ ↓ │
│ react-reconciler 0.33 with a JavaScript shadow tree │
│ ↓ atomic NDJSON commit ↑ native events │
│ transport and Bridge │
└───────┼──────────────────────────────────────┼──────────────────────┘
↓ │
┌────────────────────────── native host ───────┴─────────────────────┐
│ macOS: Swift and SwiftUI │ Windows: C# and WinUI 3 │
│ observable node store │ node registry and control map │
│ recursive native view mapping │ native controls and Grid layout │
└─────────────────────────────────┴──────────────────────────────────┘Native layout owns geometry
NatUI maps the React tree to platform declarative primitives. VStack becomes a SwiftUI VStack on macOS and a vertical native grid stack on Windows. The native toolkit owns layout proposals, focus, accessibility, theming, and control behavior.
There is no Yoga or browser layout layer. The tradeoff is intentional: applications get platform-native behavior rather than pixel-identical cross-platform rendering or CSS-style layout.
React reconciler
packages/natui uses react-reconciler mutation mode with a JavaScript shadow tree. Each instance records its id, kind, props, handlers, children, and materialization state.
Render-phase methods only construct or rearrange the shadow tree. Commit work emits protocol operations after a node attaches to a materialized parent.
The bridge buffers all operations from one React commit and sends them as one commit message. A host applies the complete batch on its UI thread before one render pass, so it never displays an intermediate commit state.
React may discard render-phase instances. Deferring protocol creation until attachment prevents abandoned nodes from leaking into the native tree.
Props and handlers
Event-handler functions remain in a JavaScript registry. The host emits an event with the node id and event name, then the bridge locates and invokes the current handler.
NatUI validates other props and deep-copies them into documented JSON values before serialization. It reports invalid nested values with their component kind and path. A commit is all-or-nothing, so serialization failure cannot partially mutate the native tree.
Controlled inputs
Native controls apply edits optimistically for immediate feedback. Each edit carries a per-node sequence number. JavaScript acknowledges the highest processed sequence on its next update.
An acknowledgement older than the host's current sequence cannot overwrite the latest local value. When the acknowledgement catches up, React becomes authoritative again, including for transformations and rejected edits.
Events run at React's discrete priority and flush synchronously. After each controlled change, the bridge checks whether React adopted the value. If not, it sends a corrective update carrying the event acknowledgement.
See controlled state and the wire protocol.
Native hosts
macOS
The Swift host stores each node in an observable model. A recursive SwiftUI view switches on the node kind without type erasure, and children use stable ids so React moves remain native moves.
The development host is a Swift Package Manager executable with native GUI
application identity. It supports the Node standard-stream mode and an
embedded JavaScriptCore mode. The reference packager wraps the release host,
application JavaScript, compatibility manifest, and native metadata in a
standard .app.
Windows
The Windows development host is an unpackaged, self-contained WinUI 3
executable. It uses a node registry and native control map. Grid-based stacks
provide the flexible tracks needed by Spacer and infinite frames.
WinUI also raises events for programmatic changes, so prop application uses guards and structural equality checks to prevent feedback loops.
The base component set has real-window Node-mode verification. The host also
embeds V8 for single-process browser bundles through @natui/core/inproc.
The reference packager publishes one portable, architecture-specific, self-contained EXE containing the application JavaScript and manifest as managed resources. The .NET single-file runtime extracts its runtime dependencies to a per-user temporary cache at launch.
Debug surface
The protocol includes four verification messages:
dumpreturns the host's actual native node tree.emitsynthesizes an interaction event.editperforms a real optimistic controlled edit.screenshotrenders the native window to a PNG and always receives success or error.
These messages allow end-to-end assertions without general-purpose UI automation. Popup layers and some focus or accessibility behavior still need dedicated native testing.
Runtime and packaging paths
NatUI currently has three launch paths:
- Node development: React runs in Node, spawns the native host, and exchanges NDJSON over standard streams. The development server rebuilds the application's local source graph and applies React Fast Refresh to the existing root, preserving compatible component state and native control identity without restarting Node or the native window.
- Raw embedded development: Both hosts evaluate an esbuild browser bundle
in-process, using system JavaScriptCore on macOS and V8 on Windows. The
bundle and host exchange protocol messages through
__natui_sendand__natui_recv. Standard input remains available as a verification debug channel. - Packaged embedded application:
pnpm package:demobuilds the same embedded runtime into a macOS.appor one Windows self-contained EXE. A generated manifest records identity, platform, architecture, protocol, minimum host API, and the SHA-256 digest ofmain.js.
The native package loader validates the manifest before JavaScript evaluation. The embedded ready handshake repeats protocol and host API validation at the runtime boundary.
runEmbedded owns the JavaScript lifecycle. Its controller can update the
root and exposes an idempotent quit() method. Shutdown synchronously unmounts
React, runs effect cleanup, sends one host quit request, rejects pending bridge
work, and removes the receive hook. Native window close uses the same path.
Current alpha boundaries
The repository includes 37 host components, including menus, toolbars, navigation, data views, and presentation.
Features still outside the current alpha scope include:
- A public animation or gesture API
- Multi-window application APIs
- Code signing, macOS notarization, installers, and automatic updates
- Single-instance application orchestration
Accessibility labels, hints, identifiers, and the native controls' default accessibility behavior are in scope.