{"id":15551,"library":"browser-metro","title":"Browser Metro Bundler","description":"browser-metro is a unique, client-side JavaScript and TypeScript bundler designed to run entirely within a web browser, typically leveraging a Web Worker for performance. Inspired by React Native's Metro bundler, it provides features like Hot Module Replacement (HMR), React Refresh support, and integration with Expo Router for file-based routing. It manages modules through a VirtualFS, performs rapid compilation of TypeScript and JSX via Sucrase transforms, and supports on-demand bundling of npm packages through an external ESM server (e.g., `https://esm.reactnative.run`). As of version 1.0.15, it emphasizes rapid development feedback loops in browser-based playgrounds and development environments. Its key differentiator is its completely client-side operation, removing the need for a Node.js build server for many common development tasks, making it ideal for interactive coding environments and sandboxes.","status":"active","version":"1.0.15","language":"javascript","source_language":"en","source_url":"https://github.com/RapidNative/reactnative-run","tags":["javascript","bundler","react-native","metro","hmr","typescript","jsx","browser","virtual-fs"],"install":[{"cmd":"npm install browser-metro","lang":"bash","label":"npm"},{"cmd":"yarn add browser-metro","lang":"bash","label":"yarn"},{"cmd":"pnpm add browser-metro","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"Bundler is a named export. It is used for one-shot bundling.","wrong":"import Bundler from 'browser-metro'; // Not a default export","symbol":"Bundler","correct":"import { Bundler } from 'browser-metro';"},{"note":"Used for watch-mode and HMR-enabled bundling. Ensure your environment supports ESM.","wrong":"const IncrementalBundler = require('browser-metro').IncrementalBundler; // Primarily ESM-focused","symbol":"IncrementalBundler","correct":"import { IncrementalBundler } from 'browser-metro';"},{"note":"Provides an in-memory file system abstraction required by the bundler.","wrong":"import { Fs as VirtualFS } from 'browser-metro'; // Incorrect alias","symbol":"VirtualFS","correct":"import { VirtualFS } from 'browser-metro';"},{"note":"Pre-configured transformer for TypeScript and JSX compilation using Sucrase.","wrong":"import { tsTransformer } from 'browser-metro';","symbol":"typescriptTransformer","correct":"import { typescriptTransformer } from 'browser-metro';"},{"note":"This is a TypeScript type definition, typically imported with `import type`.","symbol":"BundlerConfig","correct":"import type { BundlerConfig } from 'browser-metro';"}],"quickstart":{"code":"import { Bundler, VirtualFS, typescriptTransformer } from \"browser-metro\";\nimport type { FileMap } from \"browser-metro\";\n\nasync function initializeBundler() {\n  const files: FileMap = {\n    \"/index.ts\": 'import { greet } from \"./utils\";\\nconsole.log(greet(\"World\"));',\n    \"/utils.ts\": 'export function greet(name: string) { return \"Hello, \" + name; }',\n  };\n\n  const bundler = new Bundler(new VirtualFS(files), {\n    resolver: { sourceExts: [\"ts\", \"tsx\", \"js\", \"jsx\"] },\n    transformer: typescriptTransformer,\n    server: { packageServerUrl: \"https://esm.reactnative.run\" }, // Required for npm packages\n  });\n\n  try {\n    const code = await bundler.bundle(\"/index.ts\");\n    console.log(\"Bundled Code:\\n\", code);\n    // To execute in browser context, you might create a Blob URL or inject into an iframe\n    // const blob = new Blob([code], { type: 'application/javascript' });\n    // const url = URL.createObjectURL(blob);\n    // const iframe = document.createElement('iframe');\n    // iframe.src = url;\n    // document.body.appendChild(iframe);\n  } catch (error) {\n    console.error(\"Bundling failed:\", error);\n  }\n}\n\ninitializeBundler();","lang":"typescript","description":"This quickstart demonstrates how to set up a basic `Bundler` with a `VirtualFS` to bundle TypeScript code, including a dependency. It outputs the resulting self-executing JavaScript bundle, ready for execution in a browser context. It also highlights the necessary `packageServerUrl` for external npm modules."},"warnings":[{"fix":"Optimize your virtual file system content, use smaller entry points, and consider pre-bundling external dependencies if feasible. Leveraging Web Workers for the bundling process (as `browser-metro` itself does) can mitigate some impact. Monitor bundle size and parsing times.","message":"Performance for large projects can be a bottleneck. As `browser-metro` runs entirely client-side, bundling extremely large or complex codebases may lead to noticeable performance degradation, especially on less powerful devices or older browsers.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Ensure network connectivity to the configured `packageServerUrl`. For critical applications or offline usage, consider self-hosting an ESM package server or pre-packaging essential npm dependencies directly into your `VirtualFS`.","message":"`browser-metro` relies on an external ESM package server (e.g., `https://esm.reactnative.run`) for resolving and bundling npm packages. Downtime, network issues, or rate limiting from this service can prevent package resolution and bundling.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"For HMR with React Refresh, use `IncrementalBundler`, specify `reactRefreshTransformer` in your config, and enable `hmr: { enabled: true, reactRefresh: true }`. Ensure your application's runtime correctly receives and applies the `hmrUpdate` objects (e.g., via `iframe.postMessage`) and that React components follow Fast Refresh rules (e.g., named exports).","message":"Correctly configuring HMR and React Refresh requires careful setup. Incorrect transformer choices (e.g., not using `reactRefreshTransformer`) or improper client-side handling of HMR updates will prevent hot updates and lead to full page reloads.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Verify the import path is correct and the module exists in the `VirtualFS`. Ensure `resolver.sourceExts` in your `BundlerConfig` includes the file extension of the module (e.g., `['ts', 'tsx', 'js', 'jsx']`). If importing an npm package, check network connectivity to your `packageServerUrl` and that the package exists on the remote server.","cause":"The bundler could not locate the imported module. This often happens due to incorrect import paths, missing files in the `VirtualFS`, or an unconfigured `sourceExts` for the module type.","error":"Error: Module not found: Can't resolve 'some-module' in '/path/to/file.ts'"},{"fix":"Ensure you are importing and passing a valid transformer, such as `typescriptTransformer` or `reactRefreshTransformer`, to your `BundlerConfig`. If creating a custom transformer, it must adhere to the `Transformer` interface specified by `browser-metro`.","cause":"The `transformer` option in `BundlerConfig` was not correctly provided or is not a valid transformer function (e.g., `typescriptTransformer` or `reactRefreshTransformer`).","error":"TypeError: transformer is not a function"},{"fix":"Ensure all React components are named exports or default exports of named functions. Verify your HMR client-side logic correctly processes `hmrUpdate` objects from `IncrementalBundler` and applies patches to the iframe or target environment. Check for duplicate React installations if possible.","cause":"React Refresh typically fails when components are not exportable (e.g., default exports of anonymous functions), or when there are issues with multiple instances of React/React Refresh, or problems with the HMR update application logic on the client side.","error":"React Refresh: A wild runtime error occurred. (or similar HMR-related console errors)"}],"ecosystem":"npm"}