Skill: Analyze JS Bundle Size
Use source-map-explorer and Expo Atlas to visualize what's in your JavaScript bundle.
Quick Command
# React Native CLI
npx react-native bundle \
--entry-file index.js \
--bundle-output output.js \
--platform ios \
--sourcemap-output output.js.map \
--dev false --minify true && \
npx source-map-explorer output.js --no-border-checks
# Expo
EXPO_UNSTABLE_ATLAS=true npx expo export --platform ios && npx expo-atlasWhen to Use
- JS bundle seems too large
- Want to identify heavy dependencies
- Investigating startup time issues
- Before/after optimization comparison
Note: This skill involves visual treemap output (source-map-explorer, Expo Atlas). When regression checks include device flows, use
agent-devicefor app evidence; install it through the environment's approved/trusted path or ask the user if verification needs it and it is missing. Treemap analysis itself may still require exported reports, browser screenshots, or human review. Record the largest modules and before/after bundle sizes in text when asking an agent to reason about them.
Understanding Hermes Bytecode
Release builds using Hermes, the default engine in modern React Native, ship Hermes bytecode rather than raw JavaScript:
- Skips parsing at runtime
- Still benefits from smaller bundles
- Heavy imports still execute on startup
Impact of bundle size:
- Larger bytecode = longer download from store
- More imports on init path = slower TTI
Development builds fetch JS from the dev server, and non-Hermes engines have different startup tradeoffs. Smaller bytecode helps app size and startup, but startup also depends on what executes on the initialization path. Imports that eagerly touch native modules can defeat Turbo Module lazy loading and hurt TTI.
Method 1: source-map-explorer
Generate Bundle with Source Map
React Native CLI:
npx react-native bundle \
--entry-file index.js \
--bundle-output output.js \
--platform ios \
--sourcemap-output output.js.map \
--dev false \
--minify trueExpo (SDK 51+):
npx expo export --platform ios --source-maps --output-dir dist
# Bundle at: dist/ios/_expo/static/js/ios/*.js
# Source map at: dist/ios/_expo/static/js/ios/*.mapAnalyze
npx source-map-explorer output.js --no-border-checksNote: --no-border-checks needed due to Metro's non-standard source maps.
Opens browser with treemap visualization:
Bundle Treemap from source-map-explorer
The treemap shows:
- Hierarchy:
node_modules/→react-native/→Libraries/→ individual files - Size: Box area proportional to file size (KB shown in labels)
- Major components visible:
react-native(724.18 KB, 80.5%)Renderer(208.44 KB) - ReactNativeRenderer-prod.js, ReactFabric-prod.jsComponents(125.29 KB) - Touchable, ScrollView, etc.Animated(79.48 KB) - Animation systemvirtualized-lists(57.57 KB) - FlatList internals
Click on any section to drill down into that directory.
Limitation: May lose ~30% info due to mapping issues.
Method 2: Expo Atlas
More accurate for Expo projects (or with workaround for bare RN).
For Expo Projects
# Start with Atlas enabled
EXPO_UNSTABLE_ATLAS=true npx expo start --no-dev
# Or export
EXPO_UNSTABLE_ATLAS=true npx expo exportThen launch UI:
npx expo-atlas
Expo Atlas provides more accurate visualization for Expo projects, with similar treemap interface showing module sizes and dependencies.
For Non-Expo Projects
Use expo-atlas-without-expo package.
Method 3: Re.Pack Bundle Analysis (Webpack/Rspack)
If using Re.Pack:
webpack-bundle-analyzer
rspack build --analyzebundle-stats / statoscope
# Generate stats
npx react-native bundle \
--platform android \
--entry-file index.js \
--dev false \
--minify true \
--json stats.json
# Analyze
npx bundle-stats --html --json stats.jsonRsdoctor
// rspack.config.js
const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin');
module.exports = {
plugins: [
process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
].filter(Boolean),
};Run with:
RSDOCTOR=true npx react-native startWhat to Look For
Red Flags
| Finding | Problem | Solution |
|---|---|---|
| Entire library imported | Barrel exports | Use direct imports |
| Duplicate packages | Multiple versions | Dedupe in package.json |
| Dev dependencies in bundle | Incorrect imports | Check conditional imports |
| Large polyfills | Unnecessary for Hermes | Remove (see native-sdks-over-polyfills.md) |
| Moment.js with locales | Bloated date library | Switch to date-fns or dayjs |
Common Offenders
- Lodash full import: Prefer built-ins or specific imports
- Moment.js: Replace with
date-fnsordayjs - Intl polyfills: Check Hermes API and method coverage before removing them
- AWS SDK: Import specific services only
Code Examples
Identify Barrel Import Impact
// BAD: Imports entire library through barrel
import { format } from 'date-fns';
// In bundle: All of date-fns loaded
// GOOD: Direct import
import format from 'date-fns/format';
// In bundle: Only format functionComparing Bundles
source-map-explorer
# Generate baseline
npx react-native bundle ... --bundle-output baseline.js --sourcemap-output baseline.js.map
# Make changes, generate new bundle
npx react-native bundle ... --bundle-output current.js --sourcemap-output current.js.map
# Compare manually in browserRe.Pack (automated)
npx bundle-stats compare baseline-stats.json current-stats.jsonQuick Commands
React Native CLI:
# iOS bundle analysis
npx react-native bundle \
--entry-file index.js \
--bundle-output ios-bundle.js \
--platform ios \
--sourcemap-output ios-bundle.js.map \
--dev false \
--minify true && \
npx source-map-explorer ios-bundle.js --no-border-checks
# Android bundle analysis
npx react-native bundle \
--entry-file index.js \
--bundle-output android-bundle.js \
--platform android \
--sourcemap-output android-bundle.js.map \
--dev false \
--minify true && \
npx source-map-explorer android-bundle.js --no-border-checksExpo:
# Use Expo Atlas (recommended for Expo projects)
EXPO_UNSTABLE_ATLAS=true npx expo export --platform ios
npx expo-atlasRelated Skills
- bundle-barrel-exports.md - Fix barrel import issues
- bundle-tree-shaking.md - Enable dead code elimination
- bundle-library-size.md - Check library sizes before adding