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