A native Windows desktop app on a monitor with a large red Maine Coon cat resting on the desk nearby.

React Native for Windows: Real Native Desktop, No Chromium Required

The Orange Cat
The Orange Cat

There is a moment in every React Native team's life when someone asks the inevitable question: "Can we ship this on Windows too?" For years the answer was an awkward shrug toward Electron, which bundles an entire copy of Chromium and asks for hundreds of megabytes of RAM before your app even draws a button. React Native for Windows offers a different deal. Maintained by Microsoft, it is an out-of-tree platform for Meta's React Native that renders your app to real native Windows UI, controls backed by WinUI and the Windows App SDK, not a browser engine wearing a desktop costume.

The pitch is straightforward and genuinely appealing. You write React components in TypeScript, reuse most of the JavaScript business logic you already have on iOS and Android, and the framework draws native Windows widgets with native look, native accessibility through UI Automation, and a memory footprint that does not make your laptop fans spin up. Platform-specific tweaks live in *.windows.tsx files, and anything that needs to reach deep into the operating system can be written as a native module in C# or C++. It is the "learn once, write anywhere" promise extended to the Windows desktop.

This article walks through what react-native-windows actually is in its current 0.82+ era, how to scaffold a project, and how to bridge into native code when JavaScript alone is not enough.

Why Native Beats a Browser in a Box

The core distinction worth internalizing is what gets rendered. Electron and webview-based tools hand you an HTML/CSS surface inside a packaged browser. React Native for Windows hands you actual Windows controls driven by the Windows App SDK. That has real consequences:

  • Native look and feel. Your buttons, text fields, scroll views, and context menus are the genuine article, so they match the operating system and respond the way users expect.
  • Real accessibility. Components map to Windows UI Automation, so screen readers and assistive technology work without you reinventing an accessibility tree.
  • Lower overhead. There is no bundled Chromium, so apps start leaner and use far less memory than an Electron equivalent.
  • Shared code. The same React component model and JS/TS layer you use on mobile carries over, with platform-specific files for the parts that differ.

It is also worth knowing where React Native for Windows sits in the family. Microsoft maintains a sibling project, react-native-macos, that targets native AppKit on the Mac. Pair the two and a single React Native codebase can cover iOS, Android, Windows, and macOS, with platform-specific seams where each OS demands them.

The Big Shift: Fabric Is Now the Only Game

If you are coming back to this ecosystem after a while, the most important thing to absorb is the architecture change. As of release 0.82, the legacy Paper architecture was completely removed. Every app now runs exclusively on the New Architecture, known as Fabric. There is no backward-compatibility path and no opt-out.

A few practical implications follow from that:

  • The Fabric renderer targets Composition from the start but can still host islands of XAML for advanced native controls when you need them.
  • Fabric apps now default to Windows App SDK (WinAppSDK) Win32 applications, a move away from the older pure-UWP packaging.
  • Hermes is the JavaScript engine. The old Chakra engine and the legacy web-debugging path were both removed alongside Paper.

The upgrade wall is real. An existing app that has not yet migrated to the New Architecture cannot jump to 0.82 or later until it does, and any third-party native modules that are not New-Architecture-compatible will break. If you are starting fresh today, none of this is a burden, you simply begin on Fabric. If you are upgrading an older app, budget time for the migration.

What You'll Need Before You Start

This is the part where honesty matters most: builds are Windows-only. You cannot cross-compile a Windows app from macOS or Linux, which complicates CI and mixed-OS teams. You develop on Windows 10 (kept up to date) or Windows 11, and you need a fairly heavy toolchain.

The short version of the prerequisites:

  • Visual Studio 2022 with the Desktop development with C++, Universal Windows Platform development, .NET Desktop development, and Node.js development workloads.
  • The Windows 10 SDK (10.0.22621.0) and MSVC v143 build tools for x64/x86 and ARM64.
  • .NET 6.0+ SDK and Node.js 18.18.0 LTS (the recommended line).
  • Developer Mode and Long Paths both enabled in Windows.

Rather than click through all of that by hand, Microsoft ships a dependency check-and-install script. Run an elevated PowerShell and point it at the helper:

# Run in an elevated (Administrator) PowerShell
Set-ExecutionPolicy Unrestricted -Scope Process -Force
iwr -useb https://aka.ms/rnw-vs2022-deps.ps1 | iex

It verifies the workloads and SDKs you need and installs the missing pieces. It does require elevation, and the full install is multiple gigabytes, so grab a coffee.

Scaffolding Your First Windows App

The setup flow changed along with everything else. The old npx react-native-windows-init command is deprecated. The current path is to scaffold a standard React Native app with the community CLI, add the Windows dependency, then generate the native Windows project.

# 1. Init a React Native app with the community CLI
npx --yes @react-native-community/cli@latest init MyApp --version "^0.82.0"
cd MyApp

# 2. Add the Windows platform dependency
yarn add react-native-windows@^0.82.0
# or: npm install --save react-native-windows@^0.82.0

# 3. Generate the native Windows project (creates windows/ + the .sln)
npx react-native init-windows --overwrite

That third step writes the windows/ folder and a Visual Studio solution. One gotcha worth flagging: the init process overwrites your metro.config.js to enable Windows support, so if you have custom Metro configuration, back it up first and merge it back in afterward.

With the native project in place, building and launching is a single command:

npx react-native run-windows

That starts Metro, builds the native app, and launches it. From here, your App.tsx is plain React Native, the same View, Text, ScrollView, TextInput, Image, and Pressable you already know:

import React, { useState } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';

export default function App(): React.JSX.Element {
  const [count, setCount] = useState(0);

  return (
    <View style={styles.container}>
      <Text style={styles.heading}>Hello from native Windows</Text>
      <Pressable style={styles.button} onPress={() => setCount((c) => c + 1)}>
        <Text style={styles.buttonText}>Clicked {count} times</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  heading: { fontSize: 24, marginBottom: 16 },
  button: { paddingHorizontal: 20, paddingVertical: 10, backgroundColor: '#0078D4', borderRadius: 6 },
  buttonText: { color: 'white', fontSize: 16 },
});

Those components render to genuine WinUI controls. The 0.82 release sharpened a lot of these: text selection with selectionColor, ScrollView paging, TextInput textAlign, writingDirection, selection and styling, plus context menus and overflow handling.

Keeping Platform Code Tidy

When a screen needs to behave differently on Windows than on mobile, you do not litter your components with conditionals. React Native's platform-extension resolution does the work for you. Create a .windows.tsx file alongside the default one, and Metro picks the Windows variant automatically when building for Windows:

components/
  Toolbar.tsx          // iOS + Android
  Toolbar.windows.tsx  // Windows-specific implementation

Both files export the same component name with the same props, and the rest of your app imports ./components/Toolbar without caring which version it gets. For smaller divergences you can also branch at runtime with the Platform module:

import { Platform, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  panel: {
    padding: Platform.OS === 'windows' ? 12 : 8,
    // WinUI-friendly spacing on desktop, tighter on mobile
  },
});

This is the same mechanism React Native developers already use for iOS versus Android, so there is nothing new to learn, you just gain 'windows' as a value Platform.OS can report.

Reaching Into Windows with Native Modules

JavaScript will carry you a long way, but the moment you need a system API, a hardware feature, or a rich native control that the component set does not cover, you write a native module. React Native for Windows lets you author these in either C# or C++/WinRT, both using a clean, attribute-based API.

Here is a small math module written in C#:

using System;
using Microsoft.ReactNative.Managed;

[ReactModule]
class FancyMath
{
    [ReactConstant("Pi")]
    public double PI = Math.PI;

    [ReactMethod("add")]
    public double Add(double a, double b) => a + b;

    [ReactEvent]
    public Action<double> AddEvent { get; set; }
}

The same module in C++/WinRT is just as compact:

#include "NativeModules.h"

REACT_MODULE(FancyMath);
struct FancyMath {
    REACT_METHOD(Add, L"add");
    double Add(double a, double b) noexcept { return a + b; }

    REACT_EVENT(AddEvent);
    std::function<void(double)> AddEvent;
};

The attribute vocabulary mirrors across both languages: [ReactModule] / REACT_MODULE marks the class, [ReactMethod] / REACT_METHOD exposes a callable method, [ReactConstant] / REACT_GET_CONSTANTS surfaces constants, and [ReactEvent] / REACT_EVENT lets native code push events up to JavaScript. The one piece of bookkeeping to remember is registration: each module has to be wired up through a ReactPackageProvider so the runtime can discover it.

From the JavaScript side, you call it like any other native module:

import { NativeModules } from 'react-native';

const { FancyMath } = NativeModules;

async function compute(): Promise<void> {
  const sum = await FancyMath.add(2, 3); // 5
  console.log('Pi is', FancyMath.Pi);    // exposed constant
}

Do keep the maturity gap in mind here. Many community React Native modules only target iOS and Android, so Windows support is hit-or-miss. When a dependency you rely on has no Windows backend, writing the C++ or C# bridge yourself becomes part of the job. It is the price of a smaller ecosystem than RN-mobile or Electron, and worth weighing up front.

Debugging from Visual Studio

If you would rather live inside Visual Studio for the native side, that path is fully supported. Autolink your native dependencies, open the generated solution, and run from the IDE:

npx react-native autolink-windows
# then open windows/MyApp.sln, choose the Debug + x64 configuration,
# run `yarn start` in a terminal to launch Metro, and press Run.

You also get the React Native Tools extension for VS Code if you prefer that editor, and Microsoft ships a sample app, React Native Gallery, that demonstrates the full component set in a real, browsable application, an excellent reference when you want to see how a particular control behaves on Windows before wiring it into your own app.

Where It Fits, and Where It Doesn't

React Native for Windows is a strong choice when a few things are true at once. You already have a React Native mobile app and want to reach the Windows desktop while reusing most of your JS/TS, components, and team skills. You care about native look, native accessibility, and a light memory footprint more than you care about a vast plugin ecosystem. And you are a JavaScript/React shop that would rather not adopt C# and .NET MAUI or Dart and Flutter just to ship on Windows.

It is less of a fit if your team cannot build on Windows with Visual Studio, if you depend heavily on community modules that have no Windows support, or if you need to stay perfectly current with upstream React Native, because react-native-windows tracks the upstream version in lockstep and you can only be as new as the version it has shipped support for. Compared to Electron you trade ecosystem breadth for native rendering and lower overhead; compared to Tauri you trade tiny webview bundles for actual native widgets and React component reuse.

The project is healthy and actively maintained by Microsoft, with frequent releases, a large triaged issue tracker, and roughly 96,700 weekly npm downloads at the time of writing. The latest published version is 0.83.0, tracking React Native 0.83, with peer dependencies on react ^19.2.0 and react-native ^0.83.0.

The Takeaway

React Native for Windows answers the "can we ship on Windows too?" question with something better than a compromise. Your React components become real Windows controls, your JavaScript logic comes along for free, and your app behaves like a native citizen of the desktop rather than a tab pretending to be one. The cost is a Windows-only build environment, a hefty toolchain, and the occasional dip into C# or C++ when the ecosystem leaves a gap. For a JS/React team that already lives in React Native and wants a genuinely native Windows app without the Chromium tax, that is a trade well worth making, and with the New Architecture now the only path forward, there has never been a cleaner moment to start.