Visualize and analyze the JS bundle of your React Native app. Find heavy packages, duplicates and deprecated dependencies, inspect every module, and catch bundle size regressions in CI.
- π Interactive UI to explore packages, modules and their source/bundled code
- π‘ Optimization recommendations (duplicates, deprecated / outdated / dev-only packages, and more)
- π CLI to list the heaviest packages and modules
- π Compare two reports and fail CI on bundle size regressions
- π§© Works with Metro, Re.Pack and React Native DevTools (via Rozenite)
| Package | What it does | Required |
|---|---|---|
react-native-bundle-discovery |
Generates a JSON report (metro-stats.json) of your bundle |
β Yes |
react-native-bundle-discovery-ui |
Shows the report in the browser | Optional |
react-native-bundle-discovery-cli |
Analyzes and compares reports in the terminal / CI | Optional |
react-native-bundle-discovery-rozenite-plugin |
Shows the UI inside React Native DevTools | Optional |
This setup is for a standard Metro project. Using Re.Pack or Rozenite? See Other setups.
Tip
Using an AI coding agent? Skip the manual steps below. Point your agent (Claude Code, Cursor, Codex, etc.)
at the setup-react-native-bundle-discovery skill.
It installs the package and sets up Metro or Re.Pack for you:
npx skills add retyui/react-native-bundle-discoveryyarn add -D react-native-bundle-discovery # required: generates the report
yarn add -D react-native-bundle-discovery-ui # optional: browser UI
yarn add -D react-native-bundle-discovery-cli # optional: CLI// metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
+const { createSerializer } = require('react-native-bundle-discovery');
-const config = {};
+const config = {
+ serializer: {
+ customSerializer: createSerializer({
+ projectRoot: __dirname, // β οΈ In a monorepo, use the monorepo root instead
+ }),
+ },
+};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);See all options in createSerializer.
npx react-native bundle \
--entry-file index.js \
--platform ios \
--dev false \
--bundle-output ios/main.jsbundle \
--assets-dest ios/assetsThis writes metro-stats.json to your project root.
npx react-native-bundle-discovery-ui metro-stats.json # open in the browser
npx react-native-bundle-discovery-cli metro-stats.json # get recommendations in the terminal| Setup | Guide |
|---|---|
| Re.Pack (Rspack / Webpack) | Re.Pack.md |
| React Native DevTools | Rozenite plugin |
| AI coding agent | Point your agent at the setup-react-native-bundle-discovery skill |
Requires react-native-bundle-discovery-ui.
# Start a local server (default port: 8079)
npx react-native-bundle-discovery-ui metro-stats.json [--port <port>]
# Build a static HTML report (default output: .bundle-discovery)
npx react-native-bundle-discovery-ui build metro-stats.json [--output <path>]Requires react-native-bundle-discovery-cli. Run any command with --help to see all options.
| Command | Description |
|---|---|
| (none) | Show recommended optimizations for the bundle |
packages |
List all packages in the bundle |
modules |
List the heaviest modules |
compare |
Compare two reports |
# Recommended optimizations
npx react-native-bundle-discovery-cli metro-stats.json
# All packages
npx react-native-bundle-discovery-cli packages metro-stats.json [--sort size|name] [--format json|table|default]
# Heaviest modules (default --limit: 50, use 0 to show all)
# --filter accepts plain text (case-insensitive) or a regexp, e.g. --filter '/\.json/i'
npx react-native-bundle-discovery-cli modules metro-stats.json [--limit 50] [--filter <text|/regexp/>] [--sort size|name] [--format json|table|default]
# Diff two reports: total size, added/removed/changed modules and packages,
# version changes, new duplicates and new deprecated packages
npx react-native-bundle-discovery-cli compare --before main-stats.json --after pr-stats.json [--limit 50] [--format json|markdown|default]compare exits with code 1 when any check fails:
| Option | Fails when | Example |
|---|---|---|
--fail-on-increase |
The bundle grows more than the limit (bytes or % of "before") | 50KB, 0.5MB, 51200, 5% |
--max-size |
The "after" bundle is bigger than the limit | 3MB |
--fail-on |
New duplicate and/or deprecated packages appear | new-duplicates,new-deprecated |
npx react-native-bundle-discovery-cli compare --before main-stats.json --after pr-stats.json \
--fail-on-increase 50KB \
--max-size 3MB \
--fail-on new-duplicates,new-deprecatedGitHub Actions works out of the box: the markdown report is added to the job summary and failed checks are shown as error annotations.
- run: npx react-native-bundle-discovery-cli compare --before main-stats.json --after pr-stats.json --fail-on-increase 5%To post the report as a PR comment, use --format markdown.
Exported from react-native-bundle-discovery.
Creates a Metro serializer that writes the JSON report. Use it as serializer.customSerializer (see Quick start).
| Option | Type | Default | Description |
|---|---|---|---|
projectRoot |
string |
Required | Project root. |
outputJsonPath |
string |
<projectRoot>/metro-stats.json |
Where to save the report. |
includeCode |
boolean |
true |
Include source and bundled code of each module in the report (larger file). |
includeEnvs |
string[] |
[] |
Names of environment variables to include in the report. |
fetchPackagesMetadata |
boolean |
true |
Fetch package metadata (publish date, deprecation, latest version) from the npm registry. |
silent |
boolean |
false |
Disable log output. |
serializer |
Function |
Metro default serializer | Custom serializer to wrap. |
Warning
With includeCode: true (the default) the report contains your source code.
If your code is proprietary, be careful who you share the report with.
Creates a Metro resolveRequest that removes unnecessary React Native modules from release bundles
(dev builds are not affected). The CLI recommends these options when they apply to your bundle.
| Option | Type | Default | Description |
|---|---|---|---|
removeUTFSequence |
boolean |
false |
Remove the unused react-native/Libraries/UTFSequence.js module. |
removeNewRenderer |
boolean |
false |
Remove the Fabric renderer. Enable only if the New Architecture is disabled. |
// metro.config.js
const { createResolveRequest } = require("react-native-bundle-discovery");
const config = {
resolver: {
resolveRequest: createResolveRequest({
removeUTFSequence: true,
}),
},
};- expo-atlas
- expo-atlas-without-expo
- react-native-bundle-visualizer
- webpack-bundle-analyzer
- bundle-stats
- statoscope
Built with Discovery.js (views showcase, Jora syntax).
Become a financial contributor on OpenCollective or GitHub Sponsors.
MIT
