How to set up web3.js with React 18 and Webpack5 without errors
So, I wanted to develop the front end of my blockchain app with React. I performed the usual steps, such as utilizing create-react-app to install a new React project, but only to find that there are a few Webpack 5 module issues viz-a-viz web3.js.
At first, it was a disappointment. But, after googling and doing some workaround, finally, I was able to fix it. I have articulated all those steps so that you can fix the issue.
FYI: React 18 uses Webpack 5.
Why this error occurs?
Earlier Webpack supported almost all node.js modules for browser support.
Webpack <= 4 ships with polyfills for many of the node.js core modules, which are automatically applied once a module uses any of the core modules (i.e. the crypto
module). But, as of today, various modules are created for Frontend only. While this makes using modules written for node.js easy, it adds these huge polyfills to the bundle. In many cases these polyfills are unnecessary. Webpack 5 stopped automatically polyfilling these core modules and focuses on frontend-compatible modules.
The errors are related to the polyfills for these modules:
1. http
2. stream
3. crypto
4. assert
5. https
6. url
7. os
Sample Error:
Module not found: Error: Can't resolve 'stream' in ./project-path
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
- install 'stream-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "stream": false }Module not found: Error: Can't resolve 'crypto' in ./project-path
ERROR in ./node_modules/ethereumjs-util/dist.browser/account.js 71:31-48
Let’s fix the problem…
We will override the Webpack configuration of create-react-app, without ejecting, by adding these modules manually in the project.
Step 1. Install react-app-rewired and missing modules.
yarn add -D react-app-rewired crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url
Step 2. Create a new file config-overrides.js and copy the below code.
module.exports = function override(config) {
const fallback = config.resolve.fallback || {};
Object.assign(fallback, {
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"assert": require.resolve("assert"),
"http": require.resolve("stream-http"),
"https": require.resolve("https-browserify"),
"os": require.resolve("os-browserify"),
"url": require.resolve("url")
})
config.resolve.fallback = fallback; return config;
}
Now the modules are installed and the Webpack config has been modified with these modules.
Step 3. Add a polyfill.js file, copy and paste the below code and import it on the first line of the index.js file of the project import(‘./polyfill’)
import { Buffer } from 'buffer';
window.global = window;
global.Buffer = Buffer;
global.process = {
env: { DEBUG: undefined },
version: '',
nextTick: require('next-tick')
};
Final Step. Change start command in package.json file.
"start": "react-scripts start" // existing
"start": "react-app-rewired start" // use this one
If you have followed these steps, and now start the React app, it will be working seamlessly.
Bonus Tip.
If the terminal throws warnings such as:
WARNING in ./node_modules/xhr2-cookies/dist/xml-http-request.js
Module Warning (from ./node_modules/source-map-loader/dist/cjs.js):
Failed to parse source map from '/node_modules/xhr2-cookies/xml-http-request.ts' file: Error: ENOENT: no such file or directory, open '/node_modules/xhr2-cookies/xml-http-request.ts'Failed to parse source map from '/node_modules/xhr2-cookies/xml-http-request-upload.ts' file: Error: ENOENT: no such file or directory, open '/node_modules/xhr2-cookies/xml-http-request-upload.ts'
Simply add this line in the config-overrides.js file.
config.ignoreWarnings = [/Failed to parse source map/]
// final version of the filemodule.exports = function override(config) {
const fallback = config.resolve.fallback || {};
Object.assign(fallback, {
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"assert": require.resolve("assert"),
"http": require.resolve("stream-http"),
"https": require.resolve("https-browserify"),
"os": require.resolve("os-browserify"),
"url": require.resolve("url")
})
config.resolve.fallback = fallback;
config.ignoreWarnings = [/Failed to parse source map/];
return config;
}
Enjoy hacking Web3.