React native swc
⚡️ SWC-powered transformer & minifier for Metro
- 🔧 Drop-in replacement for Metro Babel transform worker and minifier - 🦀 All transformation in Rust, no Babel in sight - 🏎️ Fast: transform worker is ~8× faster & full real world bundling ~3× faster - ⚡️ Battery friendly: 15× less CPU utilization - 🚇 Feature parity with Metro: HMR, inline requires, `Platform.select()` substituion, constant folding, delta bundles etc - 🔤 Native support for Flow, TypeScript, ESM, CJS, JSX - 🧵 Worklet/Reanimated support without Babel through custom SWC plugi... The project is written primarily in TypeScript, distributed under the MIT License license, first published in 2026. Key topics include: metro, react-native, swc, swc-plugin.
react-native-swc
SWC-powered transformer for Metro
- 🔧 Drop-in replacement for Metro Babel transform worker and minifier
- 🦀 All transformation in Rust, no Babel in sight
- 🏎️ Fast: transform worker is ~8× faster & full real world bundling ~3× faster
- ⚡️ Battery friendly: 15× less CPU utilization
- 🚇 Feature parity with Metro: HMR, inline requires,
Platform.select()substituion, constant folding, delta bundles etc - 🔤 Native support for Flow, TypeScript, ESM, CJS, JSX
- 🧵 Worklet/Reanimated support without Babel through custom SWC plugin
- 🔌 Support for SWC plugins and
process.envinlining - ⚛️ Expo Plugin for seamless integration
Install
shyarn add -D @react-native-swc/core
If your app uses react-native-reanimated:
shyarn add -D @react-native-swc/worklets-plugin
Setup
Expo (managed / CNG) — config plugin
Add @react-native-swc/core to app.json:
json{ "expo": { "plugins": ["@react-native-swc/core"] } }
shnpx expo prebuild
The plugin writes (or updates) a metro.config.js wired up to withSwcTransformer. If react-native-worklets is listed in your dependencies, the worklets SWC plugin is registered automatically. Any EXPO_PUBLIC_* env vars present at metro start are inlined into the bundle, matching Expo's default Babel pipeline. If you already have a manual withSwcTransformer call, the plugin leaves your config alone.
Disable worklet auto-detection if needed:
json["@react-native-swc/core", { "worklets": false }]
Note. Expo config plugins run only during
expo prebuild(andeas build, which invokes prebuild). Runningexpo startalone does not re-run plugins.
Bare React Native / Expo (manual)
js// metro.config.js const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); const { withSwcTransformer } = require('@react-native-swc/core'); module.exports = withSwcTransformer(mergeConfig(getDefaultConfig(__dirname), {}));
For Expo without the config plugin, swap @react-native/metro-config for expo/metro-config. To match Expo's default EXPO_PUBLIC_* env-var inlining, forward those vars through swcConfig.envs:
js// metro.config.js const { getDefaultConfig } = require('expo/metro-config'); const { withSwcTransformer } = require('@react-native-swc/core'); /** @type {import('@react-native-swc/core').SwcTransformerOptions} */ const swcConfig = { envs: Object.fromEntries( Object.entries(process.env).filter(([k, v]) => k.startsWith('EXPO_PUBLIC_')), ), }; module.exports = withSwcTransformer(getDefaultConfig(__dirname), swcConfig);
process.env.EXPO_PUBLIC_FOO references in your source are then replaced with the literal value at bundle time. Anything not prefixed with EXPO_PUBLIC_ is left alone.
Worklets (react-native-reanimated) — manual
js// metro.config.js const { getDefaultConfig } = require('@react-native/metro-config'); const { withSwcTransformer } = require('@react-native-swc/core'); /** @type {import('@react-native-swc/core').SwcTransformerOptions} */ const swcConfig = { plugins: [ [ '@react-native-swc/worklets-plugin', { pluginVersion: require('react-native-worklets/package.json').version, }, ], ], }; module.exports = withSwcTransformer(getDefaultConfig(__dirname), swcConfig);
Configuration
withSwcTransformer(metroConfig, swcOptions?) exposes an intentionally narrow surface — everything that affects Metro correctness is owned by the transform worker:
tsinterface SwcTransformerOptions { plugins?: ReadonlyArray<[string, Record<string, unknown>]>; /** * `process.env.FOO`-style replacements to inline at build time. Values are * JSON-encoded for you and merged with the worker's built-ins (`NODE_ENV`, * `EXPO_OS`, …). E.g. `{ API_URL: "https://x" }` replaces * `process.env.API_URL` with the literal string `"https://x"`. */ envs?: Record<string, string>; }
Limitations
- Custom Babel plugins from
babel.config.jsare not executed. Reanimated is covered by@react-native-swc/worklets-pluginin this repo, for other use cases see SWC plugin directory. - TypeScript sources must be isolatedModules-compatible. SWC parses each file in isolation.
- Flow handling is automatic. User
.jsfiles are parsed as Flow only if they carry an@flow/@noflowpragma or if they first fail to parse as plain JavaScript.
Contributing
See CONTRIBUTING.md.
License
MIT.
Contributors
Showing top 2 contributors by commit count.
