Here’s a problem you might run into while using React Native: Let’s say you want to use the [crypto](https://nodejs.org/api/crypto.html#crypto_crypto)
module to create some hashes. It might seem natural to do something like this:
Just import a module and create a hash, right? Right? Nope!
But this doesn’t work, because crypto
is a core Node JS module, which means it’s probably C++ code bundled with the Node JS binary, not Javascript. The React Native packager can’t package it[1] along with your app’s Javascript bundle, so you get a runtime error: Unable to resolve module 'crypto'
.
This makes core modules like crypto
, [stream](https://www.npmjs.com/package/stream)
etc. and the thousands of npm modules that depend on them unusable from React Native. Fortunately, there’s a solution to this problem, but it takes some work.
If you’re familiar the module bundler Browserify, you might know that it allows you to use core Node JS modules like crypto
in the browser using polyfills[2]. So let’s try to create a standalone Javascript file that contains a polyfill for the crypto
module, and use it within our app:
1. First, install browserify
:
npm install -g browserify
2. Create a file crypto-in.js
that imports the crypto
module and simply exports it:
3. Create a standalone Javascript bundle crypto.js
using browserify
:
browserify crypto-in.js -o crypto.js
This creates a file crypto.js
, which contains some 21,500 lines of code (containing the polyfills). The last few lines contain our code from crypto-in.js
:
At this point, it might seem like we’re done, but we’re not! If you replace the line const crypto = require('crypto');
in the app code with const crypto = require('./crypto');
and try to run the app, you get the following error:
Turns out the word require
is given special treatment by the React Native packager, and you can’t redefine it to mean something else, which is what Browserify tries to do within the bundle. If you look carefully inside crypto.js
, you will notice that it always passes in a custom require
function as a parameter and never actually uses the global require
. There is a hacky but simple way to fix this problem, explained below.
4. Open the file crypto.js
in a text editor, and replace all instances of the word require
with something else e.g. reqqq
. Make sure it’s unique, so it doesn’t conflict with any existing code and mess things up.
If you save crypto.js
and reload the app now, the previous error will go away, but a new error will appear:
The packager isn’t able to find the method createHmac
, because crypto
isn’t exported properly from crypto.js
. If you look at the last few lines of the bundle, you’ll find that this is because we’re setting module.exports
not on the global modules
object, but on something else that’s passed in as a function argument:
5. To export crypto
properly from crypto.js
, we need to make a few more changes to the file:
var crypto;
at the top of the file to declare a new variable.var crypto = require('crypto');
with crypto = require('crypto');
to set the outer variable, instead of creating a local variable inside the function body.module.exports = crypto;
outside the function body to the bottom of the file, so that it references the global module
object.After the changes, crypto.js
should look something like this:
And that’s it! If you run the app now, it should all work perfectly:
Here’s a short summary of the steps it takes to use a core Node JS module like crypto
(or something that depends on it) in a React Native app:
require
keyword inside the bundle with something unique e.g. reqqq
, so that the React Native package manager leaves it alone.crypto
module correctly from the bundle, by importing it into a global variable, and setting the global module.exports
to that variable (see the modified crypto.js).Warning: The bundle generated by browserify
can be rather large in size. For instance, the file crypto.js
generated above is over 500kb in size. So keep an eye out for large bundles. Consider using a tool like Uglify to minify the bundle and reduce its size.
[1] — https://github.com/facebook/react-native/issues/1871[2] — https://github.com/substack/browserify-handbook#builtins[3] — https://github.com/facebook/react-native/issues/6253
Thanks for reading! I hope you’ve found this article helpful. Feel free to leave comments or suggestions. I would love to know if there is a better way to do this.