Metro Bundler to specjalistyczny bundler JavaScript od Meta, zaprojektowany wyłącznie dla React Native i stanowiący rdzeń frameworka. Według oficjalnej dokumentacji React Native 0.76 (2025), Metro zapewnia inkrementalne budowanie z Fast Refresh i rozwiązywanie modułów z uwzględnieniem rozszerzeń platformowych .native.js, .ios.js i .android.js. W przeciwieństwie do Webpack, Metro nie używa długożyjącego cache module graph — zamiast tego przebudowuje graf za każdym razem przy zmianie plików, co daje błyskawiczny Hot Reload podczas programowania.
Najważniejsze
Metro Bundler to bundler JavaScript z otwartym kodem źródłowym, opracowany przez Meta dla React Native i używany domyślnie we wszystkich projektach frameworka. Zadaniem Metro jest pobranie kodu JavaScript projektu wraz z zależnościami i zbudowanie jednego lub kilku bundle, które są wykonywane na urządzeniu. W przeciwieństwie do uniwersalnych bundlerów (Webpack, Rollup, Parcel), Metro jest zoptymalizowany pod kątem specyfiki programowania mobilnego: minimalny czas kompilacji, inkrementalne aktualizacje podczas programowania i poprawna praca z modułami platformowymi (natywne moduły Objective-C, Swift, Java, Kotlin). Metro jest dostarczany w ramach React Native CLI i nie wymaga osobnej instalacji. Począwszy od React Native 0.72, Metro w wersji 0.80+ obsługuje równoległą transpilację modułów, co przyspiesza kompilację na wielordzeniowych procesorach nawet o 40%.
Architektura Metro jest podzielona na trzy niezależne komponenty: Resolver, Transformer i Bundler. Resolver odpowiada za wyszukiwanie i rozwiązywanie modułów — czyta wyrażenia import/require, znajduje odpowiednie pliki z uwzględnieniem rozszerzeń platformowych i zwraca bezwzględne ścieżki. Transformer wykonuje transpilację: stosuje Babel z presetami i pluginami, przekształca JSX, TypeScript lub Flow w czysty JavaScript. Bundler to etap końcowy: przyjmuje graf zależności od Resolvera i przetransformowane pliki od Transformera, następnie serializuje je w jeden lub kilka bundle (module bundles). Podział na trzy etapy pozwala Metro na cachowanie wyników każdego kroku: jeśli zmienił się tylko jeden plik, Resolver i Bundler mogą użyć cache dla pozostałych modułów, co daje nawet 10-krotny wzrost szybkości kompilacji w trybie programowania.
Resolver w Metro implementuje algorytm rozwiązywania modułów, częściowo zgodny z Node.js module resolution. Kluczową różnicą w stosunku do Node.js jest obsługa rozszerzeń platformowych: require('./Component') szuka Component.native.js, Component.ios.js, Component.android.js w określonej kolejności priorytetu. Resolver obsługuje również pole package.json "react-native" do mapowania modułów na alternatywne implementacje — to standardowy mechanizm dla bibliotek z kodem platformowym. Jeśli plik nie zostanie znaleziony, Resolver wyrzuca błąd z pełnym stosem wyszukiwania.
Transformer wewnątrz Metro używa Babel z presetem metro-react-native-babel-preset. Standardowa konfiguracja obejmuje: przekształcanie JSX w React.createElement, obsługę TypeScript (usuwanie typów), obsługę Flow i polyfill dla nowoczesnych standardów JavaScript (async/await, optional chaining, nullish coalescing). Transformer działa w trybie równoległym: każdy moduł jest transpilowany niezależnie, wykorzystując pulę procesów roboczych (domyślnie liczba odpowiada liczbie CPU). Od Metro 0.80 obsługiwany jest Granular Transformer Cache — unieważnianie cache na poziomie pojedynczego pliku.
Jedną z kluczowych cech Metro jest obsługa rozszerzeń plików platformowych. Projekt React Native może zawierać trzy wersje jednego modułu: Component.ios.js (iOS), Component.android.js (Android) i Component.native.js (obie platformy). Przy imporcie require('./Component') Resolver automatycznie wybiera właściwą wersję w zależności od docelowej platformy kompilacji. Pozwala to pisać kod zależny od platformy bez konstrukcji warunkowych Platform.OS. Kolejność priorytetu: nazwa.platforma.js > nazwa.native.js > nazwa.js. Rozszerzenie .native.js jest używane dla kodu wspólnego działającego na obu platformach, ale niekompatybilnego z webem. Rozszerzenia .ios.js i .android.js służą do implementacji specyficznych funkcji — nawigacja, gesty, praca z systemem plików. Metro obsługuje niestandardowe rozszerzenia poprzez konfigurację resolver.sourceExts.
// metro.config.js — konfiguracja sourceExts i watchFolders
const config = {
resolver: {
sourceExts: ['jsx', 'js', 'tsx', 'ts', 'json'],
platformExtensions: ['ios', 'android', 'native'],
},
transformer: {
babelTransformerPath: require('metro-react-native-babel-transformer'),
},
watchFolders: [path.resolve('../shared')],
};
Fast Refresh to mechanizm gorącego przeładowania React Native zaimplementowany na bazie Metro. Gdy programista zmienia plik, Metro transpiluje tylko zmodyfikowany moduł i wysyła aktualizację do aplikacji przez WebSocket. Fast Refresh aktualizuje interfejs bez przeładowywania całej aplikacji i zachowuje stan React komponentów, jeśli zmieniono tylko JSX lub style. Jeśli zmieniono kod z hookami lub stanem, Fast Refresh wykonuje ponowne zamontowanie tylko zmienionego komponentu. Metro obsługuje dwa tryby: Hot Module Replacement (HMR) do wymiany modułów na gorąco oraz Live Reload do pełnego przeładowania aplikacji przy zmianie modułów natywnych lub konfiguracji. Fast Refresh jest domyślnie włączony w React Native 0.76 i nie wymaga dodatkowej konfiguracji — wystarczy uruchomić npx react-native start.
| Tryb | Prędkość | Zachowuje stan | Wyzwalacz |
|---|---|---|---|
| Fast Refresh | 200-500ms | Tak | Zmiana plików JS/TS |
| HMR | 100-300ms | Tak | Zmiana CSS/stylów |
| Live Reload | 1-3s | Nie | Zmiana native config |
| Full Rebuild | 10-60s | Nie | Zmiana podspec/build.gradle |
metro.config.js to plik konfiguracyjny Metro, znajdujący się w katalogu głównym projektu React Native. W nim konfiguruje się: resolver (sourceExts, platformExtensions, extraNodeModules), transformer (babelTransformerPath, minifierConfig), server (port, host, enableFastRefresh) i watcher (watchFolders, healthCheck). Standardowa konfiguracja Metro jest dziedziczona z pakietu @react-native/metro-config (React Native 0.76+). W przypadku projektów z monorepozytorium dodaj watchFolders — pozwoli to Metro śledzić zmiany w pakietach poza katalogiem głównym projektu. Aby skonfigurować sourceExts, dodaj 'svg', 'png', 'gql' dla niestandardowych transformerów. Metro obsługuje asynchroniczne konfiguracje przez async function — jest to przydatne do dynamicznego generowania konfiguracji na podstawie środowiska.
Optymalizacja kompilacji Metro zaczyna się od prawidłowej konfiguracji cache. Ustaw maxWorkers na liczbę CPU minus 1. Dla dystrybucji używaj resetCache na CI. Minimalizuj sourceExts — dodatkowe rozszerzenia spowalniają rozwiązywanie modułów. W przypadku dużych projektów skonfiguruj watchFolders tylko do niezbędnych katalogów. Metro obsługuje Hermes bytecode minification, który daje mniejszy rozmiar bundle niż standardowy Terser — używaj minifierPath: 'metro-minify-terser'.
Metro obsługuje tryb kompilacji bezpośrednio do Hermes bytecode — pomijając etap JavaScript AST. Podczas kompilacji z HermesTransformer moduły są kompilowane do HBC (Hermes ByteCode) zamiast plain JavaScript. Daje to: mniejszy rozmiar bundle (o 30-40%), szybsze uruchamianie aplikacji (o 20-30%) i mniejsze zużycie pamięci. Aby włączyć, ustaw hermesCommand w metro.config.js i aktywuj flagę Hermes w build.gradle (Android) lub Podfile (iOS). Kompilacja Hermes-bundle odbywa się w dwóch etapach: najpierw Metro buduje JavaScript bundle, następnie Hermes CLI konwertuje go do HBC. Od React Native 0.70, Hermes jest domyślnym silnikiem na Androidzie, na iOS wymaga jawnego włączenia.
Różnica między Metro a Webpack wynika z różnych celów. Webpack to uniwersalny bundler dla webu z ogromnym ekosystemem pluginów i loaderów. Metro to specjalistyczny bundler dla React Native, gdzie priorytetem jest szybkość kompilacji inkrementalnej i poprawna praca z modułami natywnymi. Webpack używa długożyjącego grafu modułów z inteligentnym unieważnianiem cache — daje to szybkie ponowne kompilacje w webie, ale stwarza problemy z rozszerzeniami platformowymi. Metro nie cache'uje grafu między kompilacjami, lecz buduje go za każdym razem — upraszcza to obsługę rozszerzeń platformowych i modułów Haste. Webpack obsługuje dynamiczne importy z chunkami, Metro też — przez React.lazy() i Suspense, ale z ograniczeniem do jednego aktywnego żądania. Ekosystem Metro jest znacznie mniejszy: nie ma zamiennika style-loader, css-loader, file-loader — te zadania są rozwiązywane inaczej w React Native.
Metro to jedyny właściwy wybór dla projektów React Native. Webpack nie obsługuje platform-aware resolution od razu po wyjęciu z pudełka, a konfiguracja React Native z Webpack wymaga znacznego wysiłku (haul bundler, expo web packager). Metro jest zintegrowany z React Native CLI, wspierany przez oficjalny zespół Meta i otrzymuje aktualizacje z każdym wydaniem React Native. W przypadku projektów React Native Web można użyć Webpack do kompilacji webowej, zachowując Metro do bundle mobilnych.
Często zadawane pytania
Uruchom npx react-native start --reset-cache lub usuń folder $TMPDIR/metro-* oraz katalog .metro-health-check* w katalogu głównym projektu. Alternatywnie: npx react-native clean — to polecenie czyści wszystkie pliki tymczasowe Metro, w tym cache Babel i mapę Haste. Po wyczyszczeniu pierwsza kompilacja będzie pełna (60-120 sekund), ale kolejne kompilacje inkrementalne powrócą do normalnej prędkości.
Sprawdź obecność pakietu w package.json dependencies (nie devDependencies). Jeśli moduł znajduje się w monorepozytorium, dodaj ścieżkę w watchFolders metro.config.js. Metro domyślnie nie podąża za symlinkami — używaj resolver.extraNodeModules do jawnego mapowania. Dla Yarn PnM ustaw resolver.useWatchman: false. Jeśli moduł używa rozszerzeń platformowych, upewnij się, że pliki .ios.js lub .android.js istnieją we wskazanej ścieżce.
Zwiększ maxWorkers w metro.config.js do liczby CPU minus 1. Skonfiguruj watchFolders tylko do niezbędnych katalogów. Używaj Hermes bytecode dla kompilacji produkcyjnych — przyspiesza to końcową kompilację poprzez pominięcie minifikacji JS. Do programowania włącz Fast Refresh (jest domyślnie włączony). Ogranicz sourceExts tylko do niezbędnych rozszerzeń. Rozważ podział bundle na chunki przez lazy loading.
Technicznie tak, ale nie jest to zalecane. Metro nie jest zoptymalizowane do kompilacji webowych: brak obsługi CSS, HTML, obrazów jako modułów, nie ma code splitting z dynamicznym importem na poziomie przeglądarki. Do webu używaj Webpack, Vite lub Parcel. Metro to specjalistyczne rozwiązanie dla React Native, a próba adaptacji go do webu doprowadzi do utraty wydajności i braku ekosystemu loaderów.
Re.Pack to narzędzie społecznościowe do zastąpienia Metro przez Webpack w projektach React Native. Daje dostęp do ekosystemu loaderów Webpack (css-loader, svg-loader) i zaawansowanego code splitting. Jednak Re.Pack jest trudniejszy w konfiguracji, nie jest oficjalnie wspierany przez Meta i nie gwarantuje zgodności z nowymi wersjami React Native. Metro pozostaje jedynym oficjalnie wspieranym bundlerem, co daje stabilność i gwarancję zgodności z każdym wydaniem.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również