Back to Posts

Integrating Unity with React Native

Konstantinos Egkarchos / May 30, 2025

Unity is great at making interactive scenes, Augmented and Virtual Reality. But not great at making generic apps. If the interactive graphics is just a small part of the app then maybe Unity is an overkill. A simple app framework that doesn't require a lot of resources and can load Unity when needed would be great.

In this post I'm going to show you how to integrate Unity with React Native utilising Unity's build as Unity as a Library (UaaL) feature.

Install React Native

Assuming you have set up your environment for React Native per the official documentation, you can create a new React Native project using the following command:

npx @react-native-community/cli@latest init ReactUnity

This will create a new React Native project named ReactUnity with the latest version of the React Native CLI. At the time of writing, the latest version is 0.79.2.

In that project install the react-native-unity package:

npm install @azesmway/react-native-unity

The react-native-unity package can embed a Unity project into React Native. The package offers two-way communication between React Native and Unity, so we can send messages from React Native to Unity and vice versa.

Before we continue with Unity we need to create a couple of folders inside the React Native project. In the root folder create the path unity/builds/ios and unity/builds/android. This is where the Unity project will be built for iOS and Android respectively. ReactNative folders setup

Set up Unity

Inside the Unity project, a couple of plug in files are needed to make the integration work. In the node_modules folder of @azesmway/react-native-unity, there is a folder named unity/Assets. Copy the contents of that folder into your Unity project.

Unity assets

iOS setup

Build the project for iOS.

Open the XCode project. In order to embed Unity inside React Native we need to build the UnityFramework instead of the Unity-iPhone target. To do that, we need to pack the UnityFramework with the Data folder which contains your project's assets and .NET assemblies. With the Data folder selected, at the right panel, under the Target Membership add the UnityFramework target.

XCode Data

Next, find the NativeCallProxy.h file. At the right panel, under the Target Membership set the UnityFramework header visibility to public.

XCode NativeCallProxy

Sign the UnityFramework target with a development team. Finally, set the UnityFramework target at the top and build it.

XCode sign

After a successful build, the built framework can be found inside the Products folder. Open it in Finder and copy the UnityFramework.framework inside the unity/builds/ios folder you created earlier in the React Native project. Built from XCode

Android setup

Export the project for Android into the unity/builds/android folder.

Android export

Open the exported project in Android Studio. Find the unityLibrary/src/main/AndroidManifest.xml and remove the <intent-filter> tag inside the <activity> tag. This is required as Unity will be embedded inside the React Native app, and we don't need the intent filter to launch Unity as a standalone app.

AndroidManifest

In the android directory of the React Native project, find the settings.gradle file and add the following lines to include the Unity library:

include ':unityLibrary'
project(':unityLibrary').projectDir=new File('..\\unity\\builds\\android\\unityLibrary')

settings.gradle This will include the Unity library in the React Native project.

Next, open the build.gradle and add the following lines under repositories {:

flatDir {
            dirs "${project(':unityLibrary').projectDir}/libs"
        }

This will allow Gradle to find the Unity libraries.

build.gradle

Next, append the following lines to the gradle.properties file.

unityStreamingAssets=.unity3d

This will ensure that the Unity Streaming Assets are correctly handled by the React Native project.

Finally, modify the app/src/main/res/values/strings.xml and add the following line inside the <resources> tag:

<string name="game_view_content_description">Game view</string>

This is required by Unity.

If you are using Unity 2023 or newer as I am, an additional change is required. Trying to build the project will result into the following error:

ReactUnity/node_modules/@azesmway/react-native-unity/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java:100: error: incompatible types: UnityPlayer cannot be converted to FrameLayout return unityPlayer; ^

Edit the file node_modules/@azesmway/react-native-unity/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java at line 100 instead of returning unityPlayer to

throw new NoSuchMethodException("getFrameLayout method not found. Are you using Unity 2022 or older?");

As the code exists inside the node_modules folder, this change will be lost if you update or reinstall the package.

Lastly, if the build process fails due to not finding the local.properties file, copy-paste the one from the unity/builds/android/unityLibrary folder into the android folder of the React Native project. This file contains the path to the Android SDK and is required for the build process.

Set up React Native

Back in React Native, we need to load the Unity view. We are going to use React Navigation to navigate between the React Native screens and the Unity view. Install the following packages for React Navigation:

npm install @react-navigation/native @react-navigation/stack react-native-gesture-handler react-native-safe-area-context react-native-screens

Apart from the navigation packages, the rest are dependencies for React Navigation to work properly.

Regenerate the pods for iOS to ensure that the Unity framework is linked correctly:

rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install

In React Native, we will create two screens: a Home screen with a button to open the Unity view, and the Unity view itself. Inside Unity

Edit the App.tsx file with the following code:

import React from 'react';
import { Button, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import Unity from './UnityScreen';

const Stack = createStackNavigator();

function HomeScreen({ navigation }: any) {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button
        title="Open Unity"
        onPress={()=> navigation.navigate('Unity')}
      />
    </View>
  );
}

function App(): React.JSX.Element {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Unity" component={Unity} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default App;

Next, create a new file named UnityScreen.tsx in the root of the React Native project with the following code:

// @ts-ignore
import UnityView from '@azesmway/react-native-unity';
import React, {useEffect, useRef} from 'react';
import {View} from 'react-native';

interface IMessage {
  gameObject: string;
  methodName: string;
  message: string;
}

const Unity = ({navigation}: {navigation: any}) => {
  const unityRef = useRef<UnityView>(null);

  const message: IMessage = {
    gameObject: 'ReactNativeBridge',
    methodName: 'Hello',
    message: 'Hello from React Native!',
  };

  const logMessage = () => {
    console.log(
      'unityRef.current.postMessage ===> ',
      message.gameObject,
      '>',
      message.methodName,
      '>',
      message.message,
    );
  };

  const onUnityMessage = (result: any) => {
    const message = result.nativeEvent.message;
    console.log('onUnityMessage ===> ', message);
    if(message === 'back') {
      navigation.goBack();
    }
  };

  useEffect(() => {
    setTimeout(() => {
      if (unityRef && unityRef.current) {
        logMessage();
        // @ts-ignore
        unityRef.current.postMessage(
          message.gameObject,
          message.methodName,
          message.message,
        );
      }
    }, 2000);
  }, []);

  return (
    // If you wrap your UnityView inside a parent, please take care to set dimensions to it (with `flex:1` for example).
    // See the `Know issues` part in the README.
    <View style={{flex: 1}}>
      <UnityView
        // @ts-ignore
        ref={unityRef}
        style={{flex: 1}}
        onUnityMessage={(result: any)=> onUnityMessage(result)}
      />
    </View>
  );
};

export default Unity;

Time to test!

 npm start

This will start the React Native development server. You can run the app on an iOS or Android simulator or device. For iOS, you can run the following command:

npx react-native run-ios

For Android, you can run the following command:

npx react-native run-android

The app should show a Home screen with a button to open the Unity view. When you press the button, it will navigate to the Unity view and send a message to Unity.

Unity - React Native communication

Back to the Unity project, we had a script ButtonBehavior.cs. This is a basic script that sends messages from Unity to React Native.

We are going to modify it to both send and receive messages from React Native. Rename the script to ReactNativeBridge.cs and replace its content with the following code:

using System.Runtime.InteropServices;
using UnityEngine;

public class NativeAPI
{
#if UNITY_IOS && !UNITY_EDITOR
    [DllImport("__Internal")]
    public static extern void sendMessageToMobileApp(string message);
#endif
}

public class ReactNativeBridge : MonoBehaviour
{
    public void SendBack()
    {
        switch (Application.platform)
        {
            case RuntimePlatform.Android:
            {
                using (var androidJavaClass =
                       new AndroidJavaClass("com.azesmwayreactnativeunity.ReactNativeUnityViewManager"))
                {
                    androidJavaClass.CallStatic("sendMessageToMobileApp", "back");
                }

                break;
            }
            case RuntimePlatform.IPhonePlayer:
#if UNITY_IOS && !UNITY_EDITOR
            NativeAPI.sendMessageToMobileApp("back");
#endif
                break;
        }
    }

    public void Hello(string message)
    {
        Debug.Log("Received message: " + message);
    }
}

Create a new GameObject in the Unity scene and attach the ReactNativeBridge script to it. The name of the GameObject should be ReactNativeBridge so that it matches the gameObject property in the message we send from React Native in the UnityScreen.tsx file.

Additionally, create a Button in the Unity scene and attach the SendBack method to its OnClick event. This will allow us to send a message back to React Native when the button is pressed.

Run the project again, and you should see the Home screen with a button to open the Unity view. Inside Unity when clicking the button you created to call the SendBack method, it will send a message back to React Native and navigate back to the Home screen.

Final words

This is a basic integration of Unity with React Native. You can extend the functionality by adding more methods to the ReactNativeBridge script and sending messages from React Native to Unity. My approach is to send JSON messages from React Native to Unity and parse them. This way, you can send complex data structures between the two platforms.

As far as performance is concerned, keep in mind that it will take more resources than a regular Unity app, due to the overhead of React Native. However, if you only need Unity for a small part of your app, this approach is quite efficient.