Skip to content

feat(react-native): run on React Native for Windows - #112

Draft
NathanWalker wants to merge 2 commits into
feat/react-native-macosfrom
feat/react-native-windows
Draft

NathanWalker wants to merge 2 commits into
feat/react-native-macosfrom
feat/react-native-windows

Conversation

@NathanWalker

Copy link
Copy Markdown
Contributor

Summary

@nativescript/react-native on React Native for Windows (react-native-windows 0.83, New Architecture, Hermes), with the whole Windows Runtime available from NativeScript.runOnUI worklets. Stacked on #105 (feat/react-native-macos).

const device = await NativeScript.runOnUI(() => {
  "worklet";
  return Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamily;
});

Why its own runtime

On iOS, macOS and Android the module installs into a jsi::Runtime React Native owns. On Windows none is reachable:

  • A module DLL sees React Native's runtime only through react-native-windows' JSI ABI (JsiAbiRuntime), never the engine's runtime object.
  • NativeScript's Windows runtime (@nativescript/windows-napi) binds through Node-API, not JSI.
  • react-native-worklets has no Windows implementation.

So the module (windows/NativeScriptNativeApi) runs a Hermes runtime of its own on the UI thread.

How it works

  • UIRuntime.cpp creates the runtime through the jsr_* API of the hermes.dll react-native-windows already loaded. Microtasks are explicit and drained after every script and task: without that, this Hermes never runs promise reactions for an embedder. It attaches windows-napi to the runtime's napi_env and defines Windows, XamlWindow and NSWinRT.
  • Worklets run there from the code the Worklets Babel plugin embeds (__initData.code). Arguments, captured values and results travel as JSON. console output is replayed through React Native's console, and thrown errors reject with their stack.
  • NodeApiHost.cpp is Node's half of Node-API (threadsafe functions, cleanup hooks, buffers, async work, callback scopes). hermes.dll lacks it and napi-rs needs it at registration. The module sets NAPI_HOST_MODULES=<its DLL>;<hermes.dll>, and the addon's napi-sys takes each function from the first module that exports it. That needs feat(windows-napi): take Node-API from the modules NAPI_HOST_MODULES names windows#46.
  • The addon and nswinrt.js are embedded in the module's DLL as resources. On first use the addon is extracted to %LOCALAPPDATA%\nativescript-react-native\<hash>\, so it reaches the app however it's packaged (MSIX or unpackaged). A referenced native project's extra files never reach the MSIX layout.
  • JS (src/windows.ts): init() starts the runtime and runOnUI() sends the worklet there. Windows, NSWinRT and XamlWindow are declared undefined in React Native's runtime, so a worklet can capture them. The ObjC paths in index.ts are untouched; init and runOnUI branch first.
  • Project: windows/ is react-native-windows' cpp-lib template. It uses no Windows codegen (methods register by attribute) and the installed Visual Studio's toolset (v143 or v145).

Evidence

A react-native-windows 0.83.2 C++ app (init-windows --template cpp-app), built with Visual Studio 2022 Build Tools (MSBuild directly: the RNW CLI insists on VS 18.6+), with this package linked and autolinked, run as a registered MSIX layout on Windows 11 x64:

Check Result
Sync WinRT (AnalyticsInfo, EasClientDeviceInformation) {"family":"Windows.Desktop","name":"WINDOWS_NSTUDIO","os":"WINDOWS"} in 19 ms
Arguments + captured values "Hello from Windows x 3 (object)" (Windows resolves in the worklet)
await NSWinRT.toPromise(DeviceInformation.FindAllAsync()) "2387 devices"
console.log in the worklet React Native's console.log received "logged on the UI thread Windows.Foundation.Uri"
throw in the worklet rejects with Error: thrown in the worklet

The package's JS tests are unchanged by this: the same 12 pass and the same 9 fail with and without it. Those 9 read native sources that aren't at those paths in this checkout.

Not done

  • @nativescript/windows-napi with NAPI_HOST_MODULES isn't published yet. Until then, build it from NativeScript/windows and pass /p:NativeScriptWindowsNapiAddon=…\windows_napi.dll /p:NativeScriptWindowsNapiJs=…\nswinrt.js.
  • Only x64 was built and run; ARM64 is configured but untested.
  • NativeScript globals don't exist in React Native's own runtime on Windows: native access is through runOnUI only.
  • Windowing APIs from the Windows App SDK (Microsoft.UI.Windowing.AppWindow) aren't in the runtime's system metadata. Registering the app's .winmd (NSWinRT.interop.registerWinmd) is the next step for things like setting the window title.

@nativescript/react-native now works in react-native-windows apps (0.83,
New Architecture, Hermes), with the whole Windows Runtime available from
NativeScript.runOnUI worklets.

React Native's own runtime can't host NativeScript on Windows: a module
DLL sees it only through react-native-windows' JSI ABI, NativeScript's
Windows runtime binds through Node-API, and react-native-worklets has no
Windows implementation. So the module (windows/NativeScriptNativeApi)
runs a Hermes runtime of its own on the UI thread:

- UIRuntime: creates it through the jsr_* API of the hermes.dll
  react-native-windows already loaded (explicit microtasks, drained after
  every script and task), attaches @nativescript/windows-napi to its
  napi_env, and defines Windows, XamlWindow and NSWinRT. Worklets run
  there from the code the Worklets Babel plugin embeds (__initData.code),
  with arguments, captured values and results as JSON; console output is
  replayed through React Native's console, and errors reject.
- NodeApiHost: Node's half of Node-API (threadsafe functions, cleanup
  hooks, buffers, async work, callback scopes), which hermes.dll lacks and
  the addon needs. The module sets NAPI_HOST_MODULES=<its DLL>;<hermes.dll>
  so the addon's napi-sys takes each function from the right module
  (NativeScript/windows: windows-napi support for NAPI_HOST_MODULES).
- The addon and nswinrt.js are embedded in the module's DLL as resources
  and extracted to %LOCALAPPDATA%\nativescript-react-native\<hash>\ on
  first use, so they reach the app however it's packaged.
- JS (src/windows.ts): init() starts the runtime; runOnUI() sends the
  worklet there. Windows, NSWinRT and XamlWindow are declared undefined in
  React Native's runtime so worklets can capture them.

Verified in a react-native-windows 0.83.2 C++ app built with Visual Studio
2022 Build Tools: sync WinRT calls, arguments and captured values, an
awaited WinRT async operation, console replay and error propagation.
@coderabbitai

coderabbitai Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c8bc1adb-5839-414e-a2d6-924d8af0500e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Xcode 27 ships an arm64-only libclang, so linking an x86_64
objc-metadata-generator fails. Skip that build with a warning, require
only the arm64 generator in the build scripts, and let npm packaging
skip the x86_64 generator when it was not built.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant