react-native-esbuild
The fastest bundler for React Native.
Features
- Fast – ~10-50x faster depending on project
- Tree shaking – Smaller bundles means faster apps (21% smaller for
init
project) - Compatible – Drop-in replacement for metro
- Configurable – Support for custom transformers and env variables
Sponsoring
If this library helped you, please consider sponsoring.
Installation
yarn add react-native-esbuild esbuild
Configuration
react-native
CLI plugin
Make sure react-native.config.js
exists in the root of your project, and create one if not. Add this library to the commands
section like this:
// react-native.config.js
const { commands } = require('react-native-esbuild');
module.exports = {
commands,
};
Optional: Esbuild settings
If you want to customize the esbuild configuration, for example by adding your own plugins you may do so with the createEsbuildCommands
function:
// react-native.config.js
const { createEsbuildCommands, babelPlugin } = require('react-native-esbuild');
// See https://esbuild.github.io/api/#simple-options
const commands = createEsbuildCommands((config) => ({
...config,
plugins: config.plugins.concat(
babelPlugin({
filter: /src\/my-babel-components\/.+\.[tj]sx?$/,
})
),
}));
module.exports = {
commands,
};
Optional: Use esbuild for development
- Open
package.json
in your editor and locatescripts
section. - Edit
start
script to bereact-native esbuild-start
. - Prevent metro from starting automatically by appending
--no-packager
to theios
/android
scripts.
{
"scripts": {
"android": "react-native run-android --no-packager",
"ios": "react-native run-ios --no-packager",
"start": "react-native esbuild-start"
}
}
Optional: Build production app with esbuild
Android
Set project.ext.react.bundleCommand
to esbuild-bundle
in android/app/build.gradle
:
// android/app/build.gradle
project.ext.react = [
enableHermes: false,
bundleCommand: "esbuild-bundle",
]
iOS
- Open your iOS project in Xcode manually or with
xed ios
- Select the
Build Phases
tab in your project settings. - Expand the
Bundle React Native code and images
section and addexport BUNDLE_COMMAND=esbuild-bundle
so it looks like this:
set -e
export BUNDLE_COMMAND=esbuild-bundle
export NODE_BINARY=node
../node_modules/react-native/scripts/react-native-xcode.sh
Usage
This library aims to be a plug-in replacement for the metro equivalent commands with the esbuild-
prefix.
react-native esbuild-start
Argument | Description | Default |
---|---|---|
--port |
Port to listen for http requests | 8081 |
--host |
Host to listen for http requests | 127.0.0.1 |
--projectRoot |
Path to a custom project root. | None |
--reset-cache |
Removes cached files. | N/A |
--no-interactive |
Disables interactive mode. | false |
react-native esbuild-bundle
Argument | Description | Default |
---|---|---|
--entry-file |
Path to the root JS file, either absolute or relative to JS root | index.js |
--platform |
Either ios or android |
ios |
--dev |
If false , warnings are disabled and the bundle is minified |
true |
--minify |
Allows overriding whether bundle is minified otherwise determined by dev value. |
Opposite of dev |
--bundle-output |
File name where to store the resulting bundle. | None |
--sourcemap-output |
File name where to store the sourcemap file for resulting bundle. | None |
--assets-dest |
Directory name where to store assets referenced in the bundle. | None |
--reset-cache |
Removes cached files. | N/A |
Troubleshooting
Expected "from" but found "{"
Flow syntax errors such as Esbuild doesn't natively support flow so such syntax needs to be stripped with a plugin. By default any file with @flow
or @noflow
pragmas will be stripped from flow, but you may also opt-in to flow stripping for more files by passing a custom flow syntax checker:
// react-native.config.js
const {
createEsbuildCommands,
defaultHasFlowSyntax,
syntaxAwareLoaderPlugin,
} = require('react-native-esbuild');
const FLOW_MODULES_WITHOUT_PRAGMA = ['react-native-video', 'rn-fetch-blob'];
const commands = createEsbuildCommands((config, args) => ({
...config,
plugins: config.plugins
.filter((plugin) => plugin.name !== 'syntax-aware-loader')
.concat(
syntaxAwareLoaderPlugin({
filter: /\.([mc]js|[tj]sx?)$/,
cache: args.dev,
hasFlowSyntax: (contents, filePath) =>
defaultHasFlowSyntax(contents, filePath) ||
FLOW_MODULES_WITHOUT_PRAGMA.find((m) =>
filePath.includes(`node_modules/${m}/`)
),
})
),
}));
module.exports = {
commands,
};
Limitations
Hermes engine
Hermes doesn't support crucial ES6 features like block level scoping (let
/const
) and the team doesn't seem to want to merge this feature mentioning it being a too big of a change without having good enough reasons to add it.
HMR/Fast Refresh
Esbuild doesn't support Fast Refresh or Hot Module Replacement, but this library supports live reload instead.
License
MIT © Joel Arvidsson 2022-