How to create a react app with Go support using WebAssembly in under 60 seconds
Tags: go, webassembly, react, typescript
Published: 2022-04-27 06:34:00

Run Go in the browser with react and Typescript using WebAssembly

TL;DR
```bash
$ npx create-react-app my-app --template typescript-golang
```

## Why create-react-app?

create-react-app allows us to quickly generate the boilerplate required to bootstrap a react application, providing a level of abstraction above the nitty-gritty infrastructure required to create a modern react app (Webpack, Babel, ESlint, etc.)

## Why include Go?

[Go](https://en.wikipedia.org/wiki/Go_(programming_language)) is a statically typed, compiled programming language designed at Google, it is syntactically similar to C, but with memory safety, garbage collection, structural typing, and [CSP-style concurrency](https://en.wikipedia.org/wiki/Communicating_sequential_processes).
In my case, I needed to run Go for [JSON schema validations](https://yamlschemavalidator.datree.io/), in other cases, you might want to perform a CPU-intensive task or use a [CLI tool written in Go](https://datree.io/).

## But WebAssembly is not supported in all browsers!
I thought so too. In reality, since November 2017, [WebAssembly is supported in all major browsers](https://blog.mozilla.org/en/mozilla/webassembly-in-browsers/). So as long as you don’t need to support [Internet Explorer](https://developer.mozilla.org/en-US/docs/WebAssembly#browser_compatibility), there is nothing to worry about.


![Go logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dy5w26bpkgih29cl5vn2.png)

## Let’s get down to business 😎

First, initialize a brand new create-react-app project, assuming you’re not an absolute savage, make sure to use the Typescript template 😇

```bash
$ npx create-react-app my-app --template typescript
```

Next, create a folder under _/src/LoadWasm_

```bash
$ cd my-app
$ mkdir ./src/LoadWasm`
```

create a file to extend the Window type declaration, we’ll use it soon.
_/src/LoadWasm/wasmTypes.d.ts_

```typescript
declare global {
  export interface Window {
    Go: any;
    myGolangFunction: (num1: number, num2: number) => number
  }
}

export {};
```

Copy a file used to load the WebAssembly code into the browser, it adds the Go property on the global window object and will act as a bridge between Javascript and WebAssembly.

This file is similar to [the one from the official Go repository](https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js), with a few minor tweaks, we’ll use it in the next section.

```bash
$ curl https://raw.githubusercontent.com/royhadad/cra-template-typescript-golang/main/template/src/LoadWasm/wasm_exec.js > ./src/LoadWasm/wasm_exec.js`
```

Next, we’ll create a wrapper component that will wrap our entire application and wait for WebAssembly to be loaded. This can be optimized for performance purposes, but it’s good enough for now, for simplicity’s sake.
_/src/LoadWasm/index.tsx_

```tsx
import './wasm_exec.js';
import './wasmTypes.d.ts';

import React, { useEffect } from 'react';

async function loadWasm(): Promise<void> {
  const goWasm = new window.Go();
  const result = await WebAssembly.instantiateStreaming(fetch('main.wasm'), goWasm.importObject);
  goWasm.run(result.instance);
}

export const LoadWasm: React.FC<React.PropsWithChildren<{}>> = (props) => {
  const [isLoading, setIsLoading] = React.useState(true);

  useEffect(() => {
    loadWasm().then(() => {
      setIsLoading(false);
    });
  }, []);

  if (isLoading) {
    return (
      <div>
        loading WebAssembly...
      </div>
    );
  } else {
    return <React.Fragment>{props.children}</React.Fragment>;
  }
};
```

Finally, we’ll wrap our entire application with the LoadWasm component, This will make sure no other components will load before WebAssembly is loaded
_/src/index.tsx_

```tsx
const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <LoadWasm>
      <App />
    </LoadWasm>
  </React.StrictMode>
);
```


## But wait, where is the Go code?

Start by initializing a Go module
```bash
$ mkdir ./wasm
$ cd ./wasm
$ go mod init wasm
$ go mod tidy
$ touch ./main.go
$ cd ..
```

Now we’ll use the [_syscall/js_ package](https://pkg.go.dev/syscall/js) to access the javascript Global scope, and set a function on it.
Secondly, we’ll implement a little hack to keep the Go code from terminating: opening a channel and waiting for it to finish, without ever using it 😈
This will allow us to continuously communicate with the Go code without needing to re-instantiate it every time.

_/wasm/main.go_

```go
package main

import (
   "syscall/js"
)

func myGolangFunction() js.Func {
   return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
      return args[0].Int() + args[1].Int()
   })
}

func main() {
   ch := make(chan struct{}, 0)
   js.Global().Set("myGolangFunction", myGolangFunction())
   <-ch
}
```
_Note: you may need to configure your IDE to support WebAssembly, see [VS Code guide](https://egghead.io/lessons/go-configure-go-build-constraints-in-vs-code-to-work-with-webassembly), [Intellij guide](https://www.jetbrains.com/help/go/webassembly-project.html)_

Now we can add this button somewhere in the application, perhaps in _App.tsx_
```tsx
<button onClick={() => { alert(window.myGolangFunction(2, 3)); }}>
  Click here to invoke WebAssembly!
</button>
```

## Putting it all together

Finally, we’ll alter the _package.json_ scripts to support build and hot-reload for WebAssembly.

### build:
```json
"build": "npm run build:wasm && npm run build:ts",
"build:ts": "react-scripts build",
"build:wasm": "cd wasm && GOOS=js GOARCH=wasm go build -o ../public/main.wasm && cd .. && echo \"compiled wasm successfully!\""
```

### hot reload

We’ll need a few dependencies
```bash
$ npm install watch concurrently --save-dev
```

And use them in the start script
```json
"start": "concurrently \"npm run watch:ts\" \"npm run watch:wasm\"",
"watch:ts": "react-scripts start",
"watch:wasm": "watch \"npm run build:wasm\" ./wasm",
```

Finally, we’ll run **_npm start_** and access the app on [localhost:3000](http://localhost:3000/)
A full example can be found in [this GitHub repository](https://github.com/royhadad/react-app-typescript-golang)

## Didn’t you say it’ll take 60 seconds? Liar!

![Pinocchio](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jpect82e9fvta9onzemr.jpeg)

Ok, maybe this does take a while, but fear no more! I have a cure for your laziness!
I have created a [**custom create-react-app template for typescript-golang**](https://github.com/royhadad/cra-template-typescript-golang), all you have to do is run the following command in your working directory

```bash
$ npx create-react-app my-app --template typescript-golang
```

And… boom! A working react app with Typescript & Go support, you can get right into coding 🥳



Feel free to follow & connect via [Github](https://github.com/royhadad) & [Linkedin](https://www.linkedin.com/in/roy-hadad/)