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
toreact-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
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:
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.
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:
<!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:
// 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.
Running the project
After the build, we can try to use it with any http-server. We will use servor
servor web --reload --browse
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))
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)