Babel plugin that replaces react-native imports with direct requires of the underlying modules, so only what you use ends up in the bundle.
Warning
This plugin is in an early stage. Expect rough edges and breaking changes, and please report issues if you run into any.
import { View, AppRegistry, Systrace } from 'react-native';
// ↓ becomes
const View = require('react-native/Libraries/Components/View/View').default;
const AppRegistry = require('react-native/Libraries/ReactNative/AppRegistry').AppRegistry;
const Systrace = require('react-native/Libraries/Performance/Systrace');react-native/index.js is a CommonJS barrel with a lazy require() getter for every public API. Bundlers can't tree-shake it, so a single import { View } from 'react-native' pulls in all of React Native (VirtualView, PushNotificationIOS, ...).
Bundle with only import { ReactNativeVersion } from 'react-native', before and after:
To measure your own bundle, use react-native-bundle-discovery.
React Native's modules are CommonJS. If the plugin emitted import View from '...', Babel's CommonJS transform would wrap every one in _interopRequireDefault() / _interopRequireWildcard() and add those helpers to each file. A plain require(...).default gives the same value without the extra code.
npm install --save-dev babel-plugin-react-native-direct-imports// babel.config.js
module.exports = {
presets: [
// the plugin generates deep imports on purpose
['module:@react-native/babel-preset', { disableDeepImportWarnings: true }],
],
plugins: [
process.env.NODE_ENV !== 'test' && [
'react-native-direct-imports',
{ reactNativeVersion: require('react-native/package.json').version },
],
].filter(Boolean),
};Then reset the Metro cache once (npx react-native start --reset-cache or npx expo start --clear).
reactNativeVersion picks the map from maps/ (default: the latest). Every stable release from 0.70.0 has one. The version must match exactly, otherwise the build fails, so update the plugin after upgrading React Native.
silenceBatchedUpdatesWarning (default: false) hides the warning logged once when unstable_batchedUpdates is imported on React Native 0.86+, where it does nothing and just calls the callback.
- Unknown names and type-only imports are left untouched.
- Re-exports (
export { X } from 'react-native') andrequire('react-native')in compiled CommonJS code (e.g. innode_modules) are rewritten too. Platformstays a localPlatformvariable, so Metro'sPlatform.OS/Platform.selectinlining keeps working (not for aliases likePlatform as P).import * as RN from 'react-native'andmodule.exports = require('react-native')throw a build error.- Generated paths point to React Native internals, which is why every version has its own map.
- ✅ Uniwind:
withUniwindConfigalso redirects deepreact-native/Libraries/...imports of the components it styles, soclassNamekeeps working. ⚠️ react-native-workletsbundle mode: before0.14.0,getBundleModeMetroConfigresolvesreact-nativeto its own shim, which sets up the worklet runtime. The deep paths skip it, so don't combine them onreact-native-worklets<0.14.0; upgrade to0.14.0or newer.
yarn generate-maps # all versions
yarn generate-maps 0.88.0 # specific versionsMIT

