Skip to content

[Due for payment 2026-10-23] [DX] Replace Metro with Re.Pack + a Rust transform pipeline for native builds #100202

Closed

Description

@melvin-bot

Background

Native iOS/Android bundles are built by Metro, which transforms in JavaScript and minifies with single-threaded terser. We ship ~38MB of minified JS. Every engineer bundles several times a day, and CI bundles on every build.

Problem

When an engineer or CI builds the app for iOS/Android, they wait 88-94s for a cold bundle, 11s for every warm rebuild, and 63s for a cold dev server — several times a day, for every engineer.

Solution

Adopt Re.Pack (Rspack-based). We keep the babel path only for the ~23 packages that require it (Flow-typed RN core, native-component registration) and run everything else through fast Rust tools (OXC/SWC) in parallel. Native and web then share the same React Compiler pipeline.

This also reconsolidates us onto the Rust React Compiler. Today we use the babel React Compiler on iOS/Android and the Rust React Compiler on web, and the two have logical differences. After this change, native runs the same OXC loader web already uses.

Results

Same machine, same day, identical code:

  • Prod bundle, cold: 88-94s → 54s (~1.7x faster)
  • Prod bundle, warm: 11s → 2s (~5.5x faster)
  • Dev server, cold: 63s → 47s (25% faster)
  • Dev server, warm: 8s → 4s (2x faster)
  • Minified JS: 38.1MB → 35.7MB (6.3% smaller)
  • Hermes bytecode: 45.1MB → 46.7MB (3.5% larger; known cause with a contained fix to try, and staging ManualAppStartup telemetry will show whether it matters)

Verified

Clean boots on iOS simulator, Android emulator, and HybridApp. RN DevTools fully functional. Fast Refresh and dev menu at Metro parity.

Trade-offs

  • Re.Pack is a separate bundler, not a fork of Metro, so Metro fixes do not flow into it. When a new RN version ships, things either work as-is or the Re.Pack team adds support. Tracking RN releases is what that team does at Callstack.
  • We could miss a future Metro improvement, though the items people actually ask about (tree shaking, code splitting, faster builds, caching) are already in Re.Pack.
  • Metro is the default, so libraries get tested against it first and we may hit an edge case. Config takes more work. After a brand-new RN release we may wait for a compatible Re.Pack version.
  • Metro stays in the repo and one flag switches back. Our web build already runs on the same engine, so this reduces the number of build systems rather than adding one.

Non-blocking follow-ups

  • Switch the babel step to standard @babel/parser, the parser Metro uses. No perf change expected; it removes a slow-parser edge case for good.
  • File upstream issues: hermes-parser slowness (repro in hand) and Flow support in OXC.
  • Add a small CI check for the babel allowlist. Today, a dependency update that adds a package needing the babel step fails at app launch instead of at build; the check catches it in the PR.

Prior art

A previous attempt at this was tracked in #58311, closed as not planned while blocked on callstack/repack#1153.

Reported in Slack.

Issue OwnerCurrent Issue Owner: @mallenexpensify

Activity

  1. elirangoshen commented on Sep 7, 2026

    @elirangoshen
    Contributor

    Hi, I will be in vacation from tomorrow (08.09) until 15.09
    I will find someone from my team to continue to work on this issue while I'm away

  2. kacper-mikolajczak commented on Sep 8, 2026

    @kacper-mikolajczak
    Contributor

    Hi 👋 I will be filling in for @elirangoshen here. I am resolving merge conflicts and staying in touch with C+. Let me know if you need anything

  3. elirangoshen commented on Sep 21, 2026

    @elirangoshen
    Contributor

    Hi, I also wanted to add another follow up which I mention in the pr as well
    What is your thoughts about it ?

  4. roryabraham commented on Sep 21, 2026

    @roryabraham
    Contributor

    happy to create another follow-up, but frankly the problem described in that comment isn't clear to me.

  5. collectioneur commented on Sep 22, 2026

    @collectioneur
    Contributor

    Hey! On an iOS dev build, opening Create expense via the FAB throws View config not found for component RNCViewPager and shows "page not found". It's a gap in the Re.Pack BABEL_PACKAGES allowlist: packages outside it skip babel codegen, so codegenNativeComponent() runs at runtime and falls back to the legacy path, which has no view config in bridgeless mode.

    Four packages with codegen specs are missing:

    react-native-pager-view
    react-native-gesture-handler
    @rnmapbox/maps
    @ua/react-native-airship
    I scanned the iOS and Android bundles from the dev server: these four have surviving codegenNativeComponent( after adding them the count is zero).

    Anything bundled by Re.Pack is likely affected, so dev builds (npm run ios / npm run android, standalone and HybridApp) and standalone release bundles. HybridApp release still uses Metro, so production should be fine

    Screen.Recording.2026-09-22.at.13.22.09.mov
  6. elirangoshen commented on Sep 22, 2026

    @elirangoshen
    Contributor

    Hey! On an iOS dev build, opening Create expense via the FAB throws View config not found for component RNCViewPager and shows "page not found". It's a gap in the Re.Pack BABEL_PACKAGES allowlist: packages outside it skip babel codegen, so codegenNativeComponent() runs at runtime and falls back to the legacy path, which has no view config in bridgeless mode.

    Four packages with codegen specs are missing:

    react-native-pager-view react-native-gesture-handler @rnmapbox/maps @ua/react-native-airship I scanned the iOS and Android bundles from the dev server: these four have surviving codegenNativeComponent( after adding them the count is zero).

    Anything bundled by Re.Pack is likely affected, so dev builds (npm run ios / npm run android, standalone and HybridApp) and standalone release bundles. HybridApp release still uses Metro, so production should be fine

    Screen.Recording.2026-09-22.at.13.22.09.mov

    Im working on a pr to fix it

  7. elirangoshen commented on Sep 22, 2026

    @elirangoshen
    Contributor

    Hey! On an iOS dev build, opening Create expense via the FAB throws View config not found for component RNCViewPager and shows "page not found". It's a gap in the Re.Pack BABEL_PACKAGES allowlist: packages outside it skip babel codegen, so codegenNativeComponent() runs at runtime and falls back to the legacy path, which has no view config in bridgeless mode.

    Four packages with codegen specs are missing:

    react-native-pager-view react-native-gesture-handler @rnmapbox/maps @ua/react-native-airship I scanned the iOS and Android bundles from the dev server: these four have surviving codegenNativeComponent( after adding them the count is zero).

    Anything bundled by Re.Pack is likely affected, so dev builds (npm run ios / npm run android, standalone and HybridApp) and standalone release bundles. HybridApp release still uses Metro, so production should be fine

    Screen.Recording.2026-09-22.at.13.22.09.mov

    #101878

  8. 45 remaining items

  9. melvin-bot commented on Oct 16, 2026

    @melvin-bot
    Author

    @shubham1206agra

    The solution for this issue has been 🚀 deployed to production 🚀 in version 9.5.12-1 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

    If no regressions arise, payment will be issued on 2026-10-23. 🎊

    The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the Contributor+ Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]. If no checklist is needed for this issue, you can click: [no checklist button]

  10. melvin-bot commented on Oct 19, 2026

    @melvin-bot
    Author

    @shubham1206agra Whoops! This issue is 2 days overdue. Let's get this updated quick!

  11. roryabraham commented on Oct 20, 2026

    @roryabraham
    Contributor

    @MelvinBot summarize the current status and next steps for this issue.

  12. MelvinBot commented on Oct 20, 2026

    @MelvinBot
    Contributor

    Status: Re.Pack is live for native dev builds and standalone builds. HybridApp release builds still use Metro. Both deploy-blocker fixes are now in production. The HybridApp switch was reopened on Oct 16 and is the main open item.

    Done

    Next steps

    1. elirangoshen: land the reopened HybridApp Re.Pack switch PR in the submodule, then watch staging for new Android/iOS blockers.
    2. shubham1206agra: post the C+ checklist for Match Metro's Android release assets in Re.Pack #102766 before the Oct 23 payment date. This clears the Overdue label.
    3. elirangoshen: finish the draft [No QA] Consolidate on the OXC React Compiler and delete the Babel one. It is tracked in [Due for payment 2026-10-07] [DX] Consolidate on the OXC React Compiler and delete the Babel one.
    4. elirangoshen: finish the draft [DX] Remove Metro from standalone native builds. A second PR will remove Metro from HybridApp builds once the switch is stable.
    5. Unassigned: the three follow-ups in the issue body are still open (switch to @babel/parser, file upstream issues, add a CI check for the babel allowlist).
    Open questions

    view run

  13. melvin-bot commented on Oct 21, 2026

    @melvin-bot
    Author

    @shubham1206agra Eep! 4 days overdue now. Issues have feelings too...

  14. melvin-bot commented on Oct 22, 2026

    @melvin-bot
    Author

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  15. melvin-bot commented on Oct 22, 2026

    @melvin-bot
    Author

    Payment Summary

    Resolving PRs:

    BugZero Checklist (@mallenexpensify)

    • I have confirmed assignees, roles, and Upwork contracts look correct
    • I have paid out Upwork contracts / manual NewDot requests
    • [BugZero Assignee] I have created a GH issue for creating/updating the regression test once above steps have been agreed upon
  16. mallenexpensify commented on Oct 26, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor+: @shubham1206agra due $175 via NewDot

    I'm assuming the mentions of deploy blockers don't lower the pay, comment/reopen if anyone disagrees.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionDailyKSv2Task

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions