DEV Community

Cover image for Unity WebGL + React: How I Made C# and TypeScript Talk to Each Other
Alok Krishali
Alok Krishali

Posted on

Unity WebGL + React: How I Made C# and TypeScript Talk to Each Other

I’ve seen many gaming websites where games run directly in the browser, and the experience can be surprisingly smooth. Since the web has a huge audience, I realized that many users would prefer to play a game instantly without downloading or installing anything.

That got me thinking: why not bring my Unity games to the web using Unity WebGL?

I already knew how to build games in Unity, but when I wanted to integrate a Unity WebGL application into a modern React application, I ran into a different challenge:

How do two completely different environments—Unity/C# and React/TypeScript—communicate with each other?

At first, I thought this would be a simple task.

It wasn't.

After trying different approaches, testing the communication, and fixing a few issues along the way, I finally got a working setup.

In this article, I’ll share the approach I used to connect Unity WebGL + React + TypeScript, including how I send data in both directions.


What We Are Building

Before writing any code, let's understand what we want to achieve.

My basic setup looks like this:

React + TypeScript
       ↓
react-unity-webgl
       ↓
Unity WebGL
       ↓
Unity C# Scripts
Enter fullscreen mode Exit fullscreen mode

The goal is simple.

I want React to be able to tell Unity something like:

"Start Level 5"
Enter fullscreen mode Exit fullscreen mode

And I also want Unity to send information back to React:

"Level Completed"
Enter fullscreen mode Exit fullscreen mode

So we need two-way communication:

React  →  Unity
React  ←  Unity
Enter fullscreen mode Exit fullscreen mode

Once I understood this, the implementation became much easier.


Step 1: Create the Unity Project

I started with a normal Unity project.

For this example, let's assume I have a simple game with a GameManager object.

The important part is that the GameObject we want React to communicate with must have a C# script attached to it.

For example:

using UnityEngine;

public class GameManager : MonoBehaviour
{
    public void SetPlayerName(string playerName)
    {
        Debug.Log("Player Name: " + playerName);
    }
}
Enter fullscreen mode Exit fullscreen mode

Here I created a public method called SetPlayerName.

This method will later be called from React.

The important thing to remember is:

React doesn't directly call a C# method.

React communicates with the Unity WebGL instance, and Unity then calls the method on the specified GameObject.


Step 2: Build Unity for WebGL

Next, I changed the Unity build platform to WebGL.

In Unity:

File
 → Build Settings
 → WebGL
 → Switch Platform
Enter fullscreen mode Exit fullscreen mode

Then I created a WebGL build.

The output contains files required to run the Unity game in a browser.

For example:

WebGL_Build/
├── Build/
├── TemplateData/
└── index.html
Enter fullscreen mode Exit fullscreen mode

I don't recommend manually modifying the generated Unity files unless you know exactly what you're changing.

Let Unity generate the build and let React handle displaying it.


Step 3: Create the React Application

For the frontend, I used React with TypeScript.

I created a Vite application:

npm create vite@latest react-unity-demo
Enter fullscreen mode Exit fullscreen mode

Then selected:

React
TypeScript
Enter fullscreen mode Exit fullscreen mode

After creating the project, I installed the package that handles Unity WebGL integration:

npm install react-unity-webgl
Enter fullscreen mode Exit fullscreen mode

This package makes it much easier to load and communicate with a Unity WebGL application from React.


Step 4: Load Unity Inside React

Now we need to connect our Unity WebGL build with React.

I created a Unity component.

A simplified version looks like this:

import { Unity, useUnityContext } from "react-unity-webgl";

function App() {
    const { unityProvider } = useUnityContext({
        loaderUrl: "/WebGL_Build/Build/WebGL_Build.loader.js",
        dataUrl: "/WebGL_Build/Build/WebGL_Build.data",
        frameworkUrl: "/WebGL_Build/Build/WebGL_Build.framework.js",
        codeUrl: "/WebGL_Build/Build/WebGL_Build.wasm",
    });

    return (
        <Unity
            unityProvider={unityProvider}
            style={{
                width: "100%",
                height: "600px",
            }}
        />
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The exact file names will depend on your Unity build.

This was one of the first places where I faced problems.

If the paths are incorrect, React may load successfully but Unity won't.

You might see errors such as:

404 Not Found
Enter fullscreen mode Exit fullscreen mode

So always check the browser's developer console and Network tab when the Unity build doesn't load.


Step 5: The Interesting Part — React to Unity

Now let's make React communicate with Unity.

This is where things started getting interesting for me.

The react-unity-webgl package provides a way to send messages to Unity.

For example:

const { unityProvider, sendMessage } = useUnityContext({
    loaderUrl: "...",
    dataUrl: "...",
    frameworkUrl: "...",
    codeUrl: "...",
});
Enter fullscreen mode Exit fullscreen mode

Then we can create a button:

<button
    onClick={() =>
        sendMessage(
            "GameManager",
            "SetPlayerName",
            "Alok"
        )
    }
>
    Send Name to Unity
</button>
Enter fullscreen mode Exit fullscreen mode

Let's understand what is happening here.

"GameManager"
Enter fullscreen mode Exit fullscreen mode

is the name of the GameObject in Unity.

"SetPlayerName"
Enter fullscreen mode Exit fullscreen mode

is the C# method.

"Alok"
Enter fullscreen mode Exit fullscreen mode

is the value we're sending.

So React is effectively saying:

Unity, find the GameObject called GameManager and call SetPlayerName() with this value.

Unity receives it through the WebGL communication layer and executes:

public void SetPlayerName(string playerName)
{
    Debug.Log("Player Name: " + playerName);
}
Enter fullscreen mode Exit fullscreen mode

And that's our first successful communication:

TypeScript
    ↓
Unity WebGL
    ↓
C#
Enter fullscreen mode Exit fullscreen mode

Step 6: Sending Numbers and Other Data

Strings are easy.

But real applications usually need more than strings.

For example:

Player ID
Level Number
Score
Game Settings
User Information
Mission Data
Enter fullscreen mode Exit fullscreen mode

For simple values, you can pass numbers or strings.

For more complex data, I prefer using JSON.

For example, React can send:

{
    "playerName": "Alok",
    "level": 5,
    "score": 1200
}
Enter fullscreen mode Exit fullscreen mode

On the Unity side, I can create a C# class:

[System.Serializable]
public class PlayerData
{
    public string playerName;
    public int level;
    public int score;
}
Enter fullscreen mode Exit fullscreen mode

Then deserialize the JSON inside Unity.

This approach becomes especially useful when the React application is acting as the main web interface and Unity is responsible mainly for the 3D/game experience.


Step 7: Unity to React

Now comes the other direction.

We have already done:

React → Unity
Enter fullscreen mode Exit fullscreen mode

But in a real application, Unity also needs to send information back.

For example:

Level Started
Level Completed
Player Died
Score Changed
Mission Completed
Game Loaded
Enter fullscreen mode Exit fullscreen mode

This is where a JavaScript bridge becomes useful.

The basic idea is:

Unity C#
   ↓
JavaScript
   ↓
React
Enter fullscreen mode Exit fullscreen mode

Unity can call a JavaScript function exposed by the WebGL page.

For example, conceptually:

public void LevelCompleted()
{
    // Notify the web application
}
Enter fullscreen mode Exit fullscreen mode

The JavaScript side can then forward that event to React.

In React, I can listen for the event and update the UI.

For example:

Unity
  ↓
"LevelCompleted"
  ↓
React
  ↓
Show "Congratulations!"
Enter fullscreen mode Exit fullscreen mode

This separation is useful because Unity doesn't need to know how the React UI works.

Unity only needs to say:

"The level has been completed."

React decides what to do with that information.


Step 8: A Real Example

This is where I found the architecture much more useful.

Imagine we're building an online game portal.

The React application contains:

Header
Login
Profile
Game List
Leaderboard
Game Screen
Enter fullscreen mode Exit fullscreen mode

Unity handles:

3D Game
Player
Gameplay
Physics
Animations
Game Logic
Enter fullscreen mode Exit fullscreen mode

Now imagine the user selects a game from React.

React tells Unity:

Load Level 10
Enter fullscreen mode Exit fullscreen mode

Unity loads the level.

After the player finishes it, Unity sends:

Level Completed
Score: 2500
Enter fullscreen mode Exit fullscreen mode

React receives the event and can:

Update the result screen
Save the score
Show leaderboard
Move to the next level
Enter fullscreen mode Exit fullscreen mode

Now the architecture becomes much clearer:

             React
        ┌──────────────┐
        │ UI           │
        │ Login        │
        │ Profile      │
        │ Leaderboard  │
        └──────┬───────┘
               │
         Communication
               │
        ┌──────▼───────┐
        │ Unity WebGL  │
        │              │
        │ Gameplay     │
        │ 3D          │
        │ Physics      │
        └──────────────┘
Enter fullscreen mode Exit fullscreen mode

If we add a backend later:

React
  ↓
.NET Web API
  ↓
Database

React
  ↕
Unity WebGL
Enter fullscreen mode Exit fullscreen mode

This is the architecture I am currently more interested in because it allows Unity to become part of a larger web application instead of being the entire website.


Step 9: Problems I Faced

The communication itself wasn't the only challenge.

I also faced a few common issues while putting everything together.

1. Unity WebGL files returning 404

Usually this was related to incorrect build paths.

I checked:

loaderUrl
dataUrl
frameworkUrl
codeUrl
Enter fullscreen mode Exit fullscreen mode

and made sure the generated files were actually available to React.

2. Unity loading but communication not working

In this case, I checked three things:

GameObject name
       ↓
C# method name
       ↓
Parameter type
Enter fullscreen mode Exit fullscreen mode

Even a small mismatch can cause problems.

3. React errors

I also ran into React-side issues such as incorrect hook usage and import/path problems.

My biggest lesson was simple:

Don't debug Unity and React at the same time.

First confirm that Unity WebGL works by itself.

Then confirm React can load Unity.

Finally, test communication.

Breaking the problem into these smaller steps saved me a lot of time.


What I Learned

The biggest lesson from this project was that Unity WebGL doesn't have to live alone.

Unity is very good at:

  • 3D
  • gameplay
  • physics
  • animations
  • game systems

React is very good at:

  • web UI
  • authentication
  • dashboards
  • navigation
  • forms
  • application state

Instead of trying to make one technology do everything, we can let each technology do what it is good at.

The communication layer becomes the bridge between them.

React / TypeScript
       ↕
 Communication Layer
       ↕
Unity WebGL / C#
Enter fullscreen mode Exit fullscreen mode

And once this basic communication is working, there are many possibilities—online games, training applications, digital twins, interactive 3D websites, simulations, and much more.


Final Thoughts

When I started this project, my main question was:

How can React and Unity communicate with each other?

After working through it, I realized that the difficult part isn't actually sending a message.

The more important part is designing the boundary between the two applications.

React doesn't need to know how Unity moves a character.

Unity doesn't need to know how React renders a dashboard.

They just need a clean way to exchange the information they actually need.

That was the approach that finally made the whole setup click for me.

If you're also working with Unity WebGL + React, I'd genuinely like to know how you're handling communication.

Are you using [react-unity-webgl](https://www.npmjs.com/package/react-unity-webgl), your own JavaScript bridge, or a different approach?

If you found a better way, share it in the comments—I’d love to learn from it. And if you ran into an issue while implementing this, leave a comment below. I’ll be happy to discuss it.

Thanks for reading! 🚀

Suggested by Writer:

Top comments (0)