React Native Confusion with Newer Versions

Behavioral Change with Module Resolution

Hi,

This might be a well-known issue in the React Native community, but recent versions have changed the behavior of module resolution. Don't get me wrong—it’s generally a good move.

Metro and the "exports" Field

Previously, Metro (the bundler used in React Native) didn't support the "exports" field in package.json. It was only available with the opt-in flag unstable_enablePackageExports.

With this flag enabled, Metro starts to understand and respect the "exports" field.

Here’s how it looks in Zustand’s package.json:
🔗 View on GitHub

"exports": {
  "./package.json": "./package.json",
  ".": {
    "import": {
      "types": "./esm/index.d.mts",
      "default": "./esm/index.mjs"
    },
    "default": {
      "types": "./index.d.ts",
      "default": "./index.js"
    }
  },
  "./*": {
    "import": {
      "types": "./esm/*.d.mts",
      "default": "./esm/*.mjs"
    },
    "default": {
      "types": "./*.d.ts",
      "default": "./*.js"
    }
  }
}

Roughly speaking:

  • When the import condition is met, the bundler picks the ESM build.

  • Otherwise, it falls back to the CJS build.

  • If a bundler doesn’t understand the "exports" field at all, it defaults to using CJS.

The Hermes and import.meta Problem

The issue arises because the most-used JavaScript runtime in React Native—Hermes—still does not fully support ESM. It throws an error when encountering import.meta.

My hope was that Hermes would support import.meta before unstable_enablePackageExports became stable.

Unfortunately, that didn't happen.

Now it seems that enablePackageExports is enabled by default, but Hermes still errors on import.meta.

The Workaround

From the discussion, I concluded that using the ESM build in React Native is either a bad idea or simply too early. As a workaround, I added a new condition in the exports field specifically for React Native users.

Final Thoughts

What’s concerning is that my original plan was to eventually drop CJS and fully move to ESM. But that now seems like a bad idea if the entire React Native ecosystem still relies on CJS.

Happy coding.

Reply

or to participate.