What Basalt is
Basalt runs React Native apps on the desktop — Linux, macOS and Windows — on React Native's own C++ core rather than on a browser engine.
Each desktop gets a host written against its native toolkit: GTK4 on Linux,
AppKit on macOS, Win32 with Direct2D and DirectWrite on Windows. A <View> is
a real widget on each. Text is laid out by Pango, Core Text or DirectWrite, so
Yoga sizes a paragraph the way it does on iOS and Android and narrowing the
window re-wraps it. The app ships as one binary with no bundled browser.
What runs underneath is React Native's, not a reimplementation of it: Hermes, Yoga, Fabric and the shadow tree are the same C++ that runs on a phone. Basalt supplies the part ReactCommon declares and does not define — a component registry, a mounting manager and a text layout engine per platform — which is why your components, props and layout behave as they already do.
How this differs from the alternatives
Electron and Tauri give you a web view. You write web UI, and the layout engine is a browser's. Basalt gives you the React Native component model and native widgets; there is no DOM and no CSS.
react-native-windows and react-native-macos are Microsoft's and Meta's own
desktop forks, each a separate repository with its own release cadence. Basalt
is one project covering three desktops, and deliberately uses their platform
names — macos and windows — so a library shipping Button.macos.js
resolves here without knowing Basalt exists.
Expo is not an alternative; it is the common case. A stock Expo app runs,
and basalt init writes the Metro config on expo/metro-config when it finds
one.
What works today
The components below are registered by every host and mount to real widgets.
Pressable and the Touchable* family are composed from View and work with
them.
View | borders and per-corner radii, transform, zIndex, overflow, display |
Text | laid out and measured by the platform's own text engine |
Image | files, http(s), data: URIs, decoded off the main thread, resizeMode |
ScrollView | clipping, scrollTo, scrollToEnd, onScroll reporting back to React |
TextInput | a real native editor, so input methods, selection and the clipboard come with it |
Switch, ActivityIndicator, Modal, RefreshControl |
Accessibility reaches the platform's accessible layer: accessibilityRole,
accessibilityLabel, accessibilityHint and accessibilityState are mapped,
and a <Text> calls itself a label without the app saying so.
Third-party native modules work — react-native-gesture-handler and
Reanimated both run — and an app can contribute its own C++ without publishing
a package. See Native modules.
What to expect
Basalt is published as 0.0.0-rc prereleases. Being specific about the limits
is more useful than a maturity label:
- React Native 0.86 and 0.87 are supported, and 0.87 is what CI pins. 0.83–0.85 have never been built or tested; 0.82 and older were attempted and deliberately stopped. See Supported versions.
- On 0.86 there is no Fast Refresh. A teardown leaves the host without a mounting manager, so a reload collapses it. Root-caused and fixed upstream in 0.87 — so use 0.87 if you want it.
- The first build compiles Hermes and React Native's C++ from source and takes tens of minutes. Later builds do not.
- Windows registers one fewer component than the other two, because nothing
there mounts an
ExpoImageyet.
Where to go next
- Getting started — prerequisites, one command, and what it changes.
- CLI reference — every command, option and default.
- app.json — which desktops to build, the bundle identifier, URL schemes.
- APIs — the few things Basalt adds that React Native has no equivalent for, because a phone has no such thing.
The repository's own documentation, under Contributing, is written for people working on Basalt rather than with it — but it is the most detailed account of how any of this works, and Decisions records why it is built this way.