DEV Community

Cover image for esbuild & react-native-web - I
Dalci Bagolin
Dalci Bagolin

Posted on • Edited on

esbuild & react-native-web - I

The esbuild 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 (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 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

import { View, Text } from "react-native";
export default function App() {
  return (
    <View style={...}>
      <Text>Universal React with Expo</Text>
    </View>
  );
}
Enter fullscreen mode Exit fullscreen mode

To test if is working with Expo we run the following command:

npm run web 
Enter fullscreen mode Exit fullscreen mode

Ok, everything is working as expected, thanks to Expo.

Alt Text

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
Enter fullscreen mode Exit fullscreen mode

Inside it, we will create an index.html file:

<!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>
Enter fullscreen mode Exit fullscreen mode

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:

// tsconfig.json or jsconfig.json
{
  "compilerOptions": {
    ...
    "baseUrl": ".",
    "paths": {
      "react-native": ["./node_modules/react-native-web"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

After running the command you can see the generated files in the web directory.

Alt Text

Running the project

After the build, we can try to use it with any http-server. We will use servor

servor web --reload --browse
Enter fullscreen mode Exit fullscreen mode

Alt Text

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:

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))
Enter fullscreen mode Exit fullscreen mode

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.

You can find the code used in this post in this repository.

Top comments (0)