|
| 1 | +# ESM support |
| 2 | + |
| 3 | +Libraries created with [`create-react-native-library`](./create.md) are pre-configured to work with ESM (ECMAScript Modules) out of the box. |
| 4 | + |
| 5 | +You can verify whether ESM support is enabled by checking the configuration for [`react-native-builder-bob`](./build.md) in the `package.json` file of the library: |
| 6 | + |
| 7 | +```json |
| 8 | +"react-native-builder-bob": { |
| 9 | + "source": "src", |
| 10 | + "output": "lib", |
| 11 | + "targets": [ |
| 12 | + ["commonjs", { "esm" : true }], |
| 13 | + ["module", { "esm" : true }], |
| 14 | + "typescript", |
| 15 | + ] |
| 16 | +} |
| 17 | +``` |
| 18 | + |
| 19 | +The `"esm": true` option enables ESM-compatible output. Here's what it does: |
| 20 | + |
| 21 | +- It adds the `.js` extension to the import statements in the generated files. |
| 22 | +- It creates a `package.json` file in the output directory with the content: `{ "type": "module" }` |
| 23 | + |
| 24 | +In addition, it's necessary to specify `"moduleResolution": "Bundler"` in your `tsconfig.json` file: |
| 25 | + |
| 26 | +```json |
| 27 | +{ |
| 28 | + "compilerOptions": { |
| 29 | + "moduleResolution": "Bundler" |
| 30 | + } |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +This means that you don't need to specify the file extension in the import statements. They'll be automatically added when possible during the build process. |
| 35 | + |
| 36 | +## Guidelines |
| 37 | + |
| 38 | +There are still a few things to keep in mind if you want your library to be ESM-compatible: |
| 39 | + |
| 40 | +- Avoid using default exports in your library. Named exports are recommended. Default exports produce a CommonJS module with a `default` property, which will work differently than the ESM build and can cause issues. |
| 41 | +- If the library uses platform-specific extensions (e.g., `.ios.js` or `.android.js`), the ESM output will not be compatible with Node.js. It's necessary to omit file extensions from the imports to make platform-specific extensions work, however, Node.js requires file extensions to be present. Bundlers such as Webpack (with [`resolve.fullySpecified: false`](https://webpack.js.org/configuration/module/#resolvefullyspecified)) or Metro can handle this. It's still possible to `require` the CommonJS build directly in Node.js. |
| 42 | +- Avoid using `.cjs`, `.mjs`, `.cts` or `.mts` extensions. Metro always requires file extensions in import statements when using `cjs` or `.mjs` which breaks platform-specific extension resolution. |
| 43 | +- Avoid using `"moduleResolution": "Node16"` or `"moduleResolution": "NodeNext"` in your `tsconfig.json` file. They require file extensions in import statements which breaks platform-specific extension resolution. |
| 44 | +- If you specify a `react-native` condition in `exports`, make sure that it comes before `import` or `require`. The conditions should be ordered from the most specific to the least specific: |
| 45 | + |
| 46 | + ```json |
| 47 | + "exports": { |
| 48 | + ".": { |
| 49 | + "types": "./lib/typescript/src/index.d.ts", |
| 50 | + "react-native": "./lib/modules/index.native.js", |
| 51 | + "import": "./lib/modules/index.js", |
| 52 | + "require": "./lib/commonjs/index.js" |
| 53 | + } |
| 54 | + } |
| 55 | + ``` |
0 commit comments