metro-bundler

Comprehensive expertise in React Native's Metro bundler, including configuration, optimization, custom transformers, caching strategies, and troubleshooting common bundling issues.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "metro-bundler" with this command: npx skills add anton-abyzov/specweave/anton-abyzov-specweave-metro-bundler

Metro Bundler Expert

Comprehensive expertise in React Native's Metro bundler, including configuration, optimization, custom transformers, caching strategies, and troubleshooting common bundling issues.

What I Know

Metro Fundamentals

What is Metro?

  • JavaScript bundler for React Native

  • Transforms and bundles JavaScript modules

  • Handles assets (images, fonts, etc.)

  • Provides fast refresh for development

  • Generates source maps for debugging

Key Concepts

  • Transformer: Converts source code (TypeScript, JSX) to JavaScript

  • Resolver: Locates modules in the file system

  • Serializer: Combines modules into bundles

  • Cache: Speeds up subsequent builds

Metro Configuration

Basic metro.config.js

const { getDefaultConfig } = require('expo/metro-config');

/** @type {import('expo/metro-config').MetroConfig} */ const config = getDefaultConfig(__dirname);

module.exports = config;

Custom Configuration

const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');

const defaultConfig = getDefaultConfig(__dirname);

const config = { transformer: { // Enable Babel transformer babelTransformerPath: require.resolve('react-native-svg-transformer'),

// Source map options
getTransformOptions: async () => ({
  transform: {
    experimentalImportSupport: false,
    inlineRequires: true,
  },
}),

},

resolver: { // Custom asset extensions assetExts: defaultConfig.resolver.assetExts.filter(ext => ext !== 'svg'),

// Custom source extensions
sourceExts: [...defaultConfig.resolver.sourceExts, 'svg', 'cjs'],

// Node module resolution
nodeModulesPaths: [
  './node_modules',
  '../../node_modules',  // For monorepos
],

// Custom platform-specific extensions
platforms: ['ios', 'android', 'native'],

},

server: { // Custom port port: 8081,

// Enhanced logging
enhanceMiddleware: (middleware) => {
  return (req, res, next) => {
    console.log(`Metro request: ${req.url}`);
    return middleware(req, res, next);
  };
},

},

watchFolders: [ // Watch external folders (monorepos) path.resolve(__dirname, '..', 'shared-library'), ],

resetCache: true, // Reset cache on start (dev only) };

module.exports = mergeConfig(defaultConfig, config);

Optimization Strategies

Inline Requires

// metro.config.js module.exports = { transformer: { getTransformOptions: async () => ({ transform: { inlineRequires: true, // Lazy load modules (faster startup) }, }), }, };

// Before (eager loading) import UserProfile from './UserProfile'; import Settings from './Settings';

function App() { return ( <View> {showProfile ? <UserProfile /> : <Settings />} </View> ); }

// After inline requires (lazy loading) function App() { return ( <View> {showProfile ? <require('./UserProfile').default /> : <require('./Settings').default /> } </View> ); }

Bundle Splitting (Experimental)

// metro.config.js module.exports = { serializer: { createModuleIdFactory: () => { // Generate stable module IDs for better caching return (path) => { return require('crypto') .createHash('sha1') .update(path) .digest('hex') .substring(0, 8); }; }, }, };

Asset Optimization

// metro.config.js module.exports = { transformer: { // Minify assets minifierPath: require.resolve('metro-minify-terser'), minifierConfig: { compress: { drop_console: true, // Remove console.log in production drop_debugger: true, }, output: { comments: false, }, }, },

resolver: { // Optimize asset resolution assetExts: [ 'png', 'jpg', 'jpeg', 'gif', 'webp', // Images 'mp3', 'wav', 'm4a', 'aac', // Audio 'mp4', 'mov', // Video 'ttf', 'otf', 'woff', 'woff2', // Fonts ], }, };

Custom Transformers

SVG Transformer

Install

npm install react-native-svg react-native-svg-transformer

metro.config.js

const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname);

config.transformer = { ...config.transformer, babelTransformerPath: require.resolve('react-native-svg-transformer'), };

config.resolver = { ...config.resolver, assetExts: config.resolver.assetExts.filter(ext => ext !== 'svg'), sourceExts: [...config.resolver.sourceExts, 'svg'], };

module.exports = config;

// Usage in code import Logo from './assets/logo.svg';

function App() { return <Logo width={120} height={40} />; }

Multiple File Extensions

// metro.config.js module.exports = { resolver: { // Add .web.js, .native.js for platform-specific code sourceExts: ['js', 'json', 'ts', 'tsx', 'jsx', 'web.js', 'native.js'],

// Custom resolution logic
resolveRequest: (context, moduleName, platform) => {
  if (moduleName === 'my-module') {
    // Custom module resolution
    return {
      filePath: '/custom/path/to/module.js',
      type: 'sourceFile',
    };
  }

  return context.resolveRequest(context, moduleName, platform);
},

}, };

Caching Strategies

Cache Management

Clear Metro cache

npx react-native start --reset-cache npm start -- --reset-cache # Expo

Or manually

rm -rf $TMPDIR/react-* rm -rf $TMPDIR/metro-*

Clear watchman cache

watchman watch-del-all

Clear all caches (nuclear option)

npm run clear # If configured in package.json

Cache Configuration

// metro.config.js const path = require('path');

module.exports = { cacheStores: [ // Custom cache directory { get: (key) => { const cachePath = path.join(__dirname, '.metro-cache', key); // Implement custom cache retrieval }, set: (key, value) => { const cachePath = path.join(__dirname, '.metro-cache', key); // Implement custom cache storage }, }, ],

// Reset cache on config changes resetCache: process.env.RESET_CACHE === 'true', };

Monorepo Setup

Workspaces Configuration

// metro.config.js (in app directory) const path = require('path'); const { getDefaultConfig } = require('@react-native/metro-config');

const projectRoot = __dirname; const workspaceRoot = path.resolve(projectRoot, '../..');

const config = getDefaultConfig(projectRoot);

// Watch workspace directories config.watchFolders = [workspaceRoot];

// Resolve modules from workspace config.resolver.nodeModulesPaths = [ path.resolve(projectRoot, 'node_modules'), path.resolve(workspaceRoot, 'node_modules'), ];

// Avoid hoisting issues config.resolver.disableHierarchicalLookup = false;

module.exports = config;

Symlink Handling

// metro.config.js module.exports = { resolver: { // Enable symlink support unstable_enableSymlinks: true,

// Resolve symlinked packages
resolveRequest: (context, moduleName, platform) => {
  const resolution = context.resolveRequest(context, moduleName, platform);

  if (resolution &#x26;&#x26; resolution.type === 'sourceFile') {
    // Resolve real path for symlinks
    const realPath = require('fs').realpathSync(resolution.filePath);
    return {
      ...resolution,
      filePath: realPath,
    };
  }

  return resolution;
},

}, };

Common Issues & Solutions

"Unable to resolve module"

Solution 1: Clear cache

npx react-native start --reset-cache

Solution 2: Reinstall dependencies

rm -rf node_modules npm install

Solution 3: Check import paths

Ensure case-sensitive imports match file names

import UserProfile from './userProfile'; # ❌ Wrong case import UserProfile from './UserProfile'; # ✅ Correct

Solution 4: Add to metro.config.js

module.exports = { resolver: { extraNodeModules: { 'my-module': path.resolve(__dirname, 'node_modules/my-module'), }, }, };

"Port 8081 already in use"

Find and kill process

lsof -ti:8081 | xargs kill -9

Or start on different port

npx react-native start --port 8082

Update code to use new port

adb reverse tcp:8082 tcp:8082 # Android

"Invariant Violation: Module AppRegistry is not a registered callable module"

Clear all caches

rm -rf $TMPDIR/react-* rm -rf $TMPDIR/metro-* watchman watch-del-all rm -rf node_modules npm install npx react-native start --reset-cache

"TransformError: ... SyntaxError"

// Add Babel plugin to metro.config.js module.exports = { transformer: { babelTransformerPath: require.resolve('./customBabelTransformer.js'), }, };

// customBabelTransformer.js module.exports = require('metro-react-native-babel-preset');

When to Use This Skill

Ask me when you need help with:

  • Configuring Metro bundler

  • Custom transformers (SVG, images, etc.)

  • Optimizing bundle size and startup time

  • Setting up monorepo with Metro

  • Troubleshooting "Unable to resolve module" errors

  • Clearing Metro cache effectively

  • Configuring source maps

  • Platform-specific file resolution

  • Debugging bundling performance

  • Custom asset handling

  • Port conflicts (8081)

  • Symlink resolution in monorepos

Essential Commands

Development

Start Metro bundler

npx react-native start

Start with cache cleared

npx react-native start --reset-cache

Start with custom port

npx react-native start --port 8082

Start with verbose logging

npx react-native start --verbose

Expo dev server

npx expo start

Expo with cache cleared

npx expo start -c

Debugging

Check Metro status

curl http://localhost:8081/status

Get bundle (for debugging)

curl http://localhost:8081/index.bundle?platform=ios > bundle.js

Check source map

curl http://localhost:8081/index.map?platform=ios > bundle.map

List all modules in bundle

curl http://localhost:8081/index.bundle?platform=ios&#x26;dev=false&#x26;minify=false

Cache Management

Clear Metro cache

rm -rf $TMPDIR/react-* rm -rf $TMPDIR/metro-*

Clear watchman

watchman watch-del-all

Clear all (comprehensive)

npm run clear # Custom script

Or manually:

rm -rf $TMPDIR/react-* rm -rf $TMPDIR/metro-* watchman watch-del-all rm -rf node_modules npm install

Pro Tips & Tricks

  1. Bundle Analysis

Analyze bundle size to find optimization opportunities:

Generate bundle with source map

npx react-native bundle
--platform ios
--dev false
--entry-file index.js
--bundle-output ./bundle.js
--sourcemap-output ./bundle.map

Analyze with source-map-explorer

npm install -g source-map-explorer source-map-explorer bundle.js bundle.map

  1. Environment-Specific Configuration

// metro.config.js const isDev = process.env.NODE_ENV !== 'production';

module.exports = { transformer: { minifierConfig: { compress: { drop_console: !isDev, // Remove console.log in production }, }, },

serializer: { getModulesRunBeforeMainModule: () => [ // Polyfills for production ...(!isDev ? [require.resolve('./polyfills.js')] : []), ], }, };

  1. Custom Asset Pipeline

// metro.config.js module.exports = { transformer: { // Optimize images during bundling assetPlugins: ['expo-asset/tools/hashAssetFiles'], },

resolver: { assetExts: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'],

// Custom asset resolution
resolveAsset: (dirPath, assetName, extension) => {
  const basePath = `${dirPath}/${assetName}`;

  // Try @2x, @3x variants
  const variants = ['@3x', '@2x', ''];
  for (const variant of variants) {
    const path = `${basePath}${variant}.${extension}`;
    if (require('fs').existsSync(path)) {
      return path;
    }
  }

  return null;
},

}, };

  1. Preloading Heavy Modules

// index.js import { AppRegistry } from 'react-native'; import App from './App';

// Preload heavy modules import('./src/heavyModule').then(() => { console.log('Heavy module preloaded'); });

AppRegistry.registerComponent('MyApp', () => App);

  1. Development Performance Boost

// metro.config.js const isDev = process.env.NODE_ENV !== 'production';

module.exports = { transformer: { // Skip minification in dev minifierPath: isDev ? undefined : require.resolve('metro-minify-terser'),

// Faster source maps in dev
getTransformOptions: async () => ({
  transform: {
    inlineRequires: !isDev,  // Only in production
  },
}),

},

server: { // Increase file watching performance watchFolders: isDev ? [] : undefined, }, };

Integration with SpecWeave

Configuration Management

  • Document Metro configuration in docs/internal/architecture/

  • Track bundle size across increments

  • Include bundling optimization in tasks.md

Performance Monitoring

  • Set bundle size thresholds

  • Track startup time improvements

  • Document optimization strategies

Troubleshooting

  • Maintain runbook for common Metro issues

  • Document cache clearing procedures

  • Track bundling errors in increment reports

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

technical-writing

No summary provided by upstream source.

Repository SourceNeeds Review
General

spec-driven-brainstorming

No summary provided by upstream source.

Repository SourceNeeds Review
General

kafka-architecture

No summary provided by upstream source.

Repository SourceNeeds Review
General

docusaurus

No summary provided by upstream source.

Repository SourceNeeds Review