esbuild & react-native-web - I
Tags: reactnativeweb, esbuild
Published: 2020-11-01 16:04:47

The [`esbuild`](https://esbuild.github.io/) is an extremally fast transpiler, bundler and minifier, with a very concise API. It is written in Go, which means that it is just only one compiled binary file without any dependencies.
The [`react-native-web`](https://necolas.github.io/react-native-web/docs/?path=/docs/overview-getting-started--page) (RNW) is a library that allows us to use `react-native ` primitives in the web front-end. 
Setting up tools to use RNW can be somewhat challenging for new users.
In addition to transpiling, bundling and minifying the code, which is the standard for almost all React projects, RNW has two more requests:
- Alias `react-native` to `react-native-web` (other libraries may also need to be aliased as well).
- Resolve the extensions, using `.web.js` over  `.android.js, .ios.js, .native.js`.

[The most common ways to do this](https://necolas.github.io/react-native-web/docs/?path=/docs/overview-getting-started--page) are to configure Babel and Webpack directly or to use `create-react-app` (CRA) or to use `expo`. If you need to create an alias to another library, at CRA you need to Eject, and at Expo you need to run  `expo customize:web`. In both cases, you will end up dealing with a Webpak configuration file.
Considering the speed performance of `esbuild` and the simplicity of use, could it be used with RNW? Well, let's try.

# Installing `esbuild`
Fist of all, let's install Esbuild:
```
npm install -g esbuild
```
It can be installed locally, but globally it makes more sense to me.

# Creating a new project to test Esbuild

To start, we will create a new RNW project using `create-react-native-app`, which provides a very simple template:
```
npx create-react-native-app -t blank rnw-esbuild
```
This template will also install `react-native-web`, `react-dom` and the Expo configurations files to run the RNW project. 

The template is very simple:
```javascript
import { View, Text } from "react-native";
export default function App() {
  return (
    <View style={...}>
      <Text>Universal React with Expo</Text>
    </View>
  );
}
```

To test if is working with Expo we run the following command:
```
npm run web 
```
Ok, everything is working as expected, thanks to Expo.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mmoqs98ra8vd9urhlzin.png)

Now, let's try Esbuild.

# Creating an `index.html`
To test our bundle file generated by Esbuild we need an `index.html`
First, let's create a new directory:
```
mkdir web
```
Inside it, we will create an `index.html` file:
```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
      html,body, #root {height: 100%;}
      body {overflow: hidden;}
      #root {display: flex;}
    </style>
  </head>
  <body>
    <div id="root"></div>
    <script src="./bundle.js"></script>
  </body>
</html>
```
Everything organized, let's move on.

# Using `esbuild`

### Configuring the alias
Esbuild uses a `tsconfig.json/jsconfig.json` to alias `react-native` to `react-native-web`. Therefore, we need to create or update this file as follows:
```json
// tsconfig.json or jsconfig.json
{
  "compilerOptions": {
    ...
    "baseUrl": ".",
    "paths": {
      "react-native": ["./node_modules/react-native-web"]
    }
  }
}
```
### Configuring Esbuild
The recipe to use `esbuild` with just React is:
```
esbuild app.jsx --bundle --outfile=out.js --define:process.env.NODE_ENV='"production"' --loader:.js=jsx
```
The option `--loader:.js=jsx` is only needed if `js` extension is used instead of `jsx`.
The `--define` option will convert the expression into `true` or `false` at building time, depending on the value of `process.env.NODE_ENV`.

Let's see what specific changes we need to build an RNW project:
- For an Expo project, the entry point is located in `node_modules\expo\AppEntry.js`, so, we need to change the entry point to it. 
- To define an order to resolve the file extensions we use the following option: `--resolve-extensions=.web.tsx,.web.ts,.web.jsx,.web.js,.tsx,.ts,.jsx,.js`.
- As we a running a javascript project, and not a typescript project, we need to inform Esbuild of the location of the `jsconfig.json` file, with the option `--tsconfig`. 

We can also include the options `--minify` and `--sourcemap`.

Consolidating these points, we have our final command: 
```
esbuild --bundle node_modules/expo/AppEntry.js --outfile=./web/bundle.js --resolve-extensions=.web.jsx,.web.js,.jsx,.js --loader:.js=jsx  '--define:process.env.NODE_ENV="production"' --tsconfig=jsconfig.json --minify --sourcemap
```
After running the command you can see the generated files in the `web` directory.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bx1agpuswafzt3h8hr99.png)

### Running the project
After the build, we can try to use it with any http-server. We will use [`servor`](https://github.com/lukejacksonn/servor)
```
servor web --reload --browse
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ww4tx4julst54eb08wnk.png)

That is it. For a very simple project, it worked. 

### Esbuild javascript API
It is possible to use Esbuild from a javascript API, instead of from a command line. In that case, the above command can be replaced for this file:
```javascript
require('esbuild').build({
  entryPoints: ['./node_modules/expo/AppEntry.js'],
  bundle: true,
  outfile: './web/bundle.js',
  tsconfig: 'jsconfig.json',
  define: {'process.env.NODE_ENV': 'production'},
  resolveExtensions: ['.web.jsx','.web.js','.jsx','.js',],
  minify: true,
  sourcemap: true
}).catch(() => process.exit(1))
```

# Conclusion 
The use of Babel and Webpack (directly or with Expo or CRA) involves the installation of many dependencies with hundreds or thousands of files. 
Using Esbuild, with just one binary file, a small change in a configuration file (tsconfig/jsconfig) and running one command line we were able to build a very simple RNW project.
In the next posts, we will evaluate Esbuild's performance in comparison with Expo and Metro and [build more complex projects](https://dev.to/dalcib/esbuild-react-native-web-ii-1bk3).

You can find the code used in this post in this [repository]
(https://github.com/dalcib/rnw-esbuild).