How to target windows, web and android with react native

How to target windows, web and android with react native

I was trying to create a react native app that can easily be built for Windows, Android and Web.

List of commands for creating the templates:

npx create-expo-app --template expo-template-blank-typescript
npm install --save react-native-windows@^0.76
npx react-native init-windows --overwrite --template "old/uwp-cs-app"
npm install --save react-dom react-native-web

(@0.76 was the last version that also had the same react version as expo).

Would like to have Typescript and C# (hence the templates).

I can now use npx expo start to run the app on the browser or the android and they both work great (no option for windows in the metro bundler).

I can use npx @react-native-community/cli run-windows to open the window app, but it's fully blank.

The alert statement I added inside index.ts shows up normally but if I add it inside the App component (inside a useEffect or as a plain statement) never triggers.

Did i do something wrong with the template creation or do I need edit the metro configuration?

const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');

const fs = require('fs');
const path = require('path');
const exclusionList = require('metro-config/src/defaults/exclusionList');

const rnwPath = fs.realpathSync(
  path.resolve(require.resolve('react-native-windows/package.json'), '..'),
);

//

/**
 * Metro configuration
 * https://facebook.github.io/metro/docs/configuration
 *
 * @type {import('metro-config').MetroConfig}
 */

const config = {
  //
  resolver: {
    blockList: exclusionList([
      // This stops "npx @react-native-community/cli run-windows" from causing the metro server to crash if its already running
      new RegExp(
        `${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
      ),
      // This prevents "npx @react-native-community/cli run-windows" from hitting: EBUSY: resource busy or locked, open msbuild.ProjectImports.zip or other files produced by msbuild
      new RegExp(`${rnwPath}/build/.*`),
      new RegExp(`${rnwPath}/target/.*`),
      /.*\.ProjectImports\.zip/,
    ]),
    //
  },
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

Is there a way to open some sort of dev tools (similar to a browsers) to check the xaml (or source html) of the windows application?

Index.ts

import { registerRootComponent } from "expo";
import App from "./App";

registerRootComponent(App);

Update

Once the page loads these 3 warnings pop up (only on windows),

The "EXNativeModulesProxy" native module is not exported through NativeModules; verify that expo-modules-core's native code is linked properly

The global process.env.EXPO_OS is not defined. This should be inlined by babel-preset-expo during transformation.

No native ExponentConstants module found, are you sure the expo-constants's module is linked properly?

Some relevant solutions include updating expo-module-core (which didn't seem to help) and fixing jest issues (which don't apply in this case).

Where should I register/link the missing properties?

Answer

Re-examine the MainPage.xaml.cs file (or the equivalent C# file) that sets up the React Native host. It's crucial to verify that all the props passed to the ReactRootView constructor are correct.

ComponentName is the most likely culprit now. The ComponentName passed to ReactRootView must exactly match the name you registered your root component with using AppRegistry.registerComponent in your JavaScript code. If they don't match, React Native won't know which component to render.

_reactRootView = new ReactRootView(host, "YourAppName"); // Make sure "YourAppName" matches your AppRegistry.registerComponent

Double-check index.js or index.ts:

import { AppRegistry } from 'react-native';
import App from './App'; // Or whatever your root component is
import { name as appName } from './app.json'; // If using Expo

AppRegistry.registerComponent(appName, () => App); // Ensure appName matches

If you're using Expo, the appName is usually defined in app.json. Verify it's correct. If you are not using expo, make sure the first parameter on the registerComponent method is the same name as the ComponentName in MainPage.xaml.cs.

Steps

Project Initialization with React Native CLI:

npx react-native init YourAppName --template react-native-template-typescript
cd YourAppName

Add Windows Support:

npx react-native-windows-init --overwrite --language cs

Add Web Support:

React Native for Web is already included in newer React Native projects. You shouldn't need extra steps here. If not:

npm install react-dom react-native-web

Make sure your index.js (or index.ts) properly handles platform differences for rendering:

import { AppRegistry, Platform } from 'react-native';
import App from './App'; 
import { name as appName } from './app.json'; 

AppRegistry.registerComponent(appName, () => App);

if (Platform.OS === 'web') {
    const rootTag = document.getElementById('root') || document.getElementById('main');
    AppRegistry.runApplication(appName, { rootTag });
}

Verify MainPage.xaml.cs and App Registration:

_reactRootView = new ReactRootView(host, appName); // appName must match!

Run the application:

npx react-native run-android  // Android
npx react-native run-windows // Windows
npx react-native start     // Web (then open browser to indicated URL)

Enjoyed this article?

Check out more content on our blog or follow us on social media.

Browse more articles