Skip to content

Instantly share code, notes, and snippets.

@parshap
Last active August 4, 2024 07:39
Show Gist options
  • Save parshap/e3063d9bf6058041b34b26b7166fd6bd to your computer and use it in GitHub Desktop.
Save parshap/e3063d9bf6058041b34b26b7166fd6bd to your computer and use it in GitHub Desktop.
Running Node Modules in React Native

Running Node Modules in React Native

How to use packages that depend on Node.js core modules in React Native.

See the node-libs-react-native library as a convenience for implementing this method.

Node.js Core Modules

Node has several modules that are automatically available via require(): http, crypto, etc. While these modules are available in Node, they are not automatically available in other environments such as browsers and React Native.

Browsers

"Node-style" code (using modules and require()) that targets browsers is usually compiled using tools such as Webpack or Browserify. These compilers implement require() and other functionality normally available in Node but not browsers, including Node core modules implemented for browser usage.

React Native

Similar to the browsers scenario, React Native compiles "Node-style" code to run in the React Native environment via the React Native Packager. The packager is similar to Webpack or Browserify, but with some minor differences, including having no official documentation or best practice for running Node core module.

Why

Usually, there are React Native-specific counterparts to the Node core modules that provide the same functionality, e.g., react-native-crypto. So why does one need Node core modules to work in React Native? The answer is cross-platform code and the vast npm ecosystem. It's convenient to be able to use the same modules in React Native as in Node and browsers.

Prior Work

There are several approaches to running Node core modules in React Native.

Webpack for React Native

While it's possible to use Webpack with React Native, it's not a widely used approach and as long as Facebook uses React Native Packager internally, React Native will work best with React Native Packager.

rn-nodeify

rn-nodeify works by editing all package.json files in node_modules to add the react-native field. Similar to the browser field in package.json, the react-native field tells the React Native Packager to substitute one modules with another (e.g., crypto with react-native-crypto). rn-nodeify also applies various other "hacks" to some sources files. See the rn-nodeify readme for more information.

This approach is less than ideal because it involves editing files in the node_modules directory and must be run after every time dependencies are changed.

ReactNativify

ReactNativify documents how to use a custom React Native Packager transformer and the [babel-plugin-rewrite-require][babel rewrite] Babel plugin to support Node core modules. While this solution is robust, being wholly encapsulated by the packager without depending on editing files in node_modules, it's not ideal as it involves maintaining a complicated transformer implementation and keeping it up to date with [Packager's implementation][packager transformer]. babel-plugin-rewrite-require also has slightly different behavior than Webpack and Browserify, not supporting require() calls with an expression (such as require('cyrp' + 'to') -- though this case can be supported using the throwForNonStringLiteral option).

metro.config.js and extraNodeModules

The best solution to running Node core modules in React Native is a built-in React Native Packager configuration option: extraNodeModules. This feature allows specifying modules that should be globally available as Node core modules. To use this configuration option, add a metro.config.js file in the root directory of your React Native project:

module.exports = {
  resolver: {
    extraNodeModules: {
      crypto: require('react-native-cyrpto'),
    },
  },
};

For extra convenience, you can use the node-libs-react-native package to make all modules available:

module.exports = {
  resolver: {
    extraNodeModules: require('node-libs-react-native'),
  },
};

Global Environment

While the above solution will make it possible to require() Node core modules, "Node-style" code also expects the global environment to be in a specific state. The React Native global environment should be mutated to meet these expectations. See node-libs-react-native/globals for an example of doing this.

@parshap
Copy link
Author

parshap commented Dec 18, 2019

Updated! Thanks.

FWIW I have the node-libs-react-native library that should help with using extraNodeModules and I'd like it to be the source of truth for documentation on this approach. This gist is more historical. Would appreciate any help updating the documentation here: https://github.com/parshap/node-libs-react-native

@pcowgill
Copy link

@parshap Noted! Thanks! Having the link over to that repo here is very helpful.

@pcowgill
Copy link

@parshap Oh, and FYI this gist has pretty good SEO on google, so I think it's safe to assume most people will find this gist first before that repo.

I would suggest removing this line:

"not supporting require() calls with an expression (such as require('cyrp' + 'to'))."

...since ReactNativify does support that if you use the throwForNonStringLiteral setting

@parshap
Copy link
Author

parshap commented Dec 18, 2019

Thanks! I vaguely remember trying to use throwForNonStringLiteral and still having issues with it, but I've added a mention of it to the gist.

I also added a link to the node-libs-react-native library to the top of the gist. Any help improving the documentation or implementation of node-libs-react-native would be greatly appreciated as I'm not currently working on any React Native projects myself.

@pcowgill
Copy link

Thanks! One last thing to change in this gist: the second metro.config.js example is missing the nested resolver part.

@parshap
Copy link
Author

parshap commented Dec 19, 2019

Ah thank you, fixed. I wish I could give others write access to gists!

@pcowgill
Copy link

@parshap You could consider turning the gist into a link over to a repo that just has a README with the contents of this gist?

@mateus-oliveira
Copy link

How I can import HTTPS lib with node-libs-react-native?

@parshap
Copy link
Author

parshap commented Jun 7, 2020

@radelcom
Copy link

please help! i am trying LDAPjs with RN and getting error with 'net' module even though 'netjs' is mocked in node-libs-react-native.

@joeyfigaro
Copy link

@radelcom same

@radelcom
Copy link

@joeyfigaro i managed to get the error solved. I used different packages and set it as a resolver under metro.config.js

I played around with react-native-tls and react-native-tcp-socket. However, if your LDAP server require sasl authentication, LDAP.js does not support it if im not mistaken.

extraNodeModules: { ...require('node-libs-react-native'), // net: require.resolve('react-native-tls'), //require.resolve('react-native-tcp-socket'), // tls: require.resolve('react-native-tls'), fs: require.resolve('react-native-fs'), },

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