Skip to main content

RAM Bundles and Inline Requires

Reduce your React Native app’s startup time by using RAM (Random Access Module) bundles and inline requires to load JavaScript code on-demand.

What are RAM Bundles?

RAM bundles split your JavaScript bundle into multiple files, allowing the app to load only the modules it needs at startup, deferring the rest until required.

Traditional Bundle

index.bundle (5MB)
├── App code
├── Dependencies
├── Screens (all)
└── Utilities (all)
All code loads at startup, increasing time-to-interactive.

RAM Bundle

index.bundle (small startup bundle)
├── js-modules/
│   ├── 0.js (Home screen)
│   ├── 1.js (Profile screen)
│   ├── 2.js (Heavy utility)
│   └── ...
Only essential code loads at startup; modules load on-demand.

Benefits

  • Faster Startup: Only load essential modules initially
  • Reduced Memory: Lower initial memory footprint
  • Better User Experience: App becomes interactive sooner
  • Scalable: Benefits increase with app size

Enabling RAM Bundles

Android

Modify android/app/build.gradle:
project.ext.react = [
    enableHermes: true,
    bundleCommand: "ram-bundle",
    extraPackagerArgs: []
]

apply from: "../../node_modules/react-native/react.gradle"
Or configure in react.gradle:
def bundleCommand = "ram-bundle"
def bundleConfig = "metro.config.js"

iOS

Modify the bundle phase script in Xcode:
1

Open Xcode project

Open ios/YourApp.xcworkspace.
2

Edit build phase

Select your target > Build Phases > Bundle React Native code and images.
3

Update script

Change the script to use ram-bundle:
export BUNDLE_COMMAND="ram-bundle"
export NODE_BINARY=node
../node_modules/react-native/scripts/react-native-xcode.sh
Alternatively, modify ios/YourApp/AppDelegate.mm:
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

Metro Configuration

Configure Metro bundler for RAM bundles in metro.config.js:
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');

const config = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true, // Enable inline requires
      },
    }),
  },
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

Inline Requires

Inline requires defer module loading until they’re actually needed.

Without Inline Requires

import React from 'react';
import { View, Text } from 'react-native';
import HeavyComponent from './HeavyComponent';
import RarelyUsedUtil from './RarelyUsedUtil';

// All imports load at startup
const App = () => {
  return (
    <View>
      <Text>App</Text>
    </View>
  );
};
Everything loads immediately, even unused modules.

With Inline Requires

import React from 'react';
import { View, Text } from 'react-native';

const App = () => {
  return (
    <View>
      <Text>App</Text>
    </View>
  );
};

// Load only when needed
const openHeavyScreen = () => {
  const HeavyComponent = require('./HeavyComponent').default;
  // Use HeavyComponent
};

const useRareUtil = () => {
  const RarelyUsedUtil = require('./RarelyUsedUtil').default;
  return RarelyUsedUtil.process();
};
Modules load on-demand, reducing startup time.

Automatic Inline Requires

Babel plugin automatically converts imports to inline requires:
npm install --save-dev babel-plugin-transform-inline-requires
Update babel.config.js:
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    'transform-inline-requires',
  ],
};
Now all non-top-level imports automatically become inline requires:
// Before transformation
import { useEffect } from 'react';
import MyComponent from './MyComponent';

function App() {
  useEffect(() => {
    console.log('mounted');
  }, []);
  return <MyComponent />;
}

// After transformation
import { useEffect } from 'react';

function App() {
  const MyComponent = require('./MyComponent').default;
  useEffect(() => {
    console.log('mounted');
  }, []);
  return <MyComponent />;
}
Combine inline requires with navigation:

React Navigation

import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

const App = () => {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        component={require('./screens/Home').default}
      />
      <Stack.Screen
        name="Profile"
        component={require('./screens/Profile').default}
      />
      <Stack.Screen
        name="Settings"
        // Lazy load heavy screens
        component={require('./screens/Settings').default}
      />
    </Stack.Navigator>
  );
};

React Navigation with getComponent

import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

const App = () => {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        getComponent={() => require('./screens/Home').default}
      />
      <Stack.Screen
        name="Profile"
        getComponent={() => require('./screens/Profile').default}
      />
    </Stack.Navigator>
  );
};

Conditional Requires

Load modules based on conditions:
import { Platform } from 'react-native';

const getAnalytics = () => {
  if (Platform.OS === 'ios') {
    return require('./analytics/ios').default;
  } else {
    return require('./analytics/android').default;
  }
};

const analytics = getAnalytics();

Measuring Impact

Before Optimization

const startTime = Date.now();

import('./App').then(() => {
  const loadTime = Date.now() - startTime;
  console.log(`App loaded in ${loadTime}ms`);
});

Monitor Bundle Size

# Generate bundle
npx react-native bundle \
  --platform android \
  --dev false \
  --entry-file index.js \
  --bundle-output output.bundle \
  --ram-bundle

# Check size
ls -lh output.bundle
ls -lh js-modules/

Startup Time Profiling

import { performance } from 'react-native';

performance.mark('app-start');

// In your App component
useEffect(() => {
  performance.mark('app-interactive');
  performance.measure(
    'startup-time',
    'app-start',
    'app-interactive'
  );
  
  const measure = performance.getEntriesByName('startup-time')[0];
  console.log(`Startup time: ${measure.duration}ms`);
}, []);

Best Practices

Do Inline Require

  • Heavy screens/components
  • Rarely used utilities
  • Platform-specific code
  • Large third-party libraries
  • Analytics and tracking code
const openCamera = () => {
  const Camera = require('react-native-camera').RNCamera;
  // Use camera
};

Don’t Inline Require

  • Core dependencies (React, React Native)
  • Frequently used components
  • Small utilities
  • Critical path code
// Keep these as imports
import React from 'react';
import { View, Text } from 'react-native';

Hermes + RAM Bundles

Combine Hermes bytecode with RAM bundles for maximum optimization:
// android/app/build.gradle
project.ext.react = [
    enableHermes: true,
    bundleCommand: "ram-bundle",
    hermesCommand: "../../node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc"
]
Benefits:
  • Bytecode compilation is faster than JavaScript parsing
  • RAM bundles reduce initial load
  • Combined: significantly faster startup

Debugging RAM Bundles

Inspect Module Loading

if (__DEV__) {
  const originalRequire = require;
  global.require = function(moduleName) {
    console.log(`Loading module: ${moduleName}`);
    return originalRequire(moduleName);
  };
}

Bundle Analyzer

Analyze your bundle composition:
npm install --save-dev react-native-bundle-visualizer

npx react-native-bundle-visualizer

Common Issues

Module Not Found

Ensure correct path in require:
// Wrong
const Component = require('MyComponent').default;

// Right
const Component = require('./MyComponent').default;

Hot Reloading

Inline requires work with Fast Refresh:
const MyComponent = require('./MyComponent').default;
// Fast Refresh still works

Static Imports in Render

Avoid require in render method:
// Bad: requires on every render
const App = () => {
  const Component = require('./Component').default;
  return <Component />;
};

// Good: require once
const Component = require('./Component').default;
const App = () => <Component />;

Performance Gains

Typical improvements with RAM bundles + inline requires:
  • Startup time: 20-40% faster
  • Initial memory: 15-30% lower
  • Time to interactive: 25-50% faster
Gains scale with app size and complexity.

Next Steps

Build docs developers (and LLMs) love