Module not found: Error: Can't resolve 'crypto'

Fix Webpack 5 'Module not found: Can't resolve crypto' for @solana/web3.js

Webpack 5 dropped Node polyfills, so @solana/web3.js breaks in the browser. Here's how to add the right fallbacks or move crypto calls to the server.

You added @solana/web3.js to your React or Next.js app, ran npm start, and got slammed with Module not found: Error: Can't resolve 'crypto'. The dev server might've even started — the error shows up when webpack tries to bundle the browser build. This happens because Webpack 5 removed the automatic Node.js polyfills that Webpack 4 shipped. Solana's library still imports crypto, buffer, and stream, expecting them to be there.

The fix depends on which build tool you're using. CRA, Vite, Next.js, and raw webpack all handle this differently. Let's go through the three causes I see most often on support tickets.

Cause 1: Webpack 5 has no built-in Node polyfills for the browser

This is the one behind 90 percent of these errors. In Webpack 4, if a browser bundle imported crypto, webpack quietly swapped in a polyfill. Webpack 5 killed that behavior. Now it just gives up and throws Module not found.

If you're on a Create React App project you can't eject from, use react-app-rewired plus react-app-rewired's config override hook. If you're on a bare webpack config, edit webpack.config.js directly.

Install the polyfill packages first:

npm install --save-dev crypto-browserify stream-browserify buffer process

Then update your webpack config. You should see these entries under resolve.fallback:

const webpack = require('webpack');

module.exports = {
  resolve: {
    fallback: {
      crypto: require.resolve('crypto-browserify'),
      stream: require.resolve('stream-browserify'),
      buffer: require.resolve('buffer/'),
      process: require.resolve('process/browser'),
    },
  },
  plugins: [
    new webpack.ProvidePlugin({
      Buffer: ['buffer', 'Buffer'],
      process: 'process/browser',
    }),
  ],
};

After saving, restart the dev server. If your terminal clears and recompiles without the crypto error, you're done. If you now see Can't resolve 'buffer', add the buffer fallback shown above — they usually come as a pair.

One gotcha: newer versions of crypto-browserify pull in vm-browserify. If webpack complains about vm, add vm: require.resolve('vm-browserify') to the same fallback object.

Cause 2: You're on Vite and the error reads differently

Vite throws a similar error but the message is Module 'crypto' has been externalized for browser compatibility. People paste that into Google, land on webpack threads, and waste an afternoon. Vite doesn't use webpack at all.

The real fix on Vite is to alias the module. Open vite.config.ts and add:

import { defineConfig } from 'vite';
import { nodePolyfills } from 'vite-plugin-node-polyfills';

export default defineConfig({
  plugins: [
    nodePolyfills({
      globals: { Buffer: true, global: true, process: true },
      protocolImports: true,
    }),
  ],
});

Install it with npm i -D vite-plugin-node-polyfills. Restart the dev server. The externalization warning should be gone and Connection from @solana/web3.js should now work in the browser.

Don't try the resolve.alias route in Vite for this specific error. It works for some packages but Solana's dependency graph pulls in too many Node builtins for a single alias to cover.

Cause 3: The code importing crypto shouldn't run in the browser at all

Here's the case nobody writes about. If you're doing keypair signing, transaction serialization, or Keypair.fromSecretKey in client-side code, you've got a bigger problem than webpack configs. You're shipping private key handling to the user's browser, which is a security disaster no matter how you polyfill it.

I've seen this on dApps where a dev copies a Node.js script straight into a React component. The proper move is to move those calls behind an API route. On Next.js, use a route in app/api/sign/route.ts. On Express, a simple POST endpoint. The client sends the transaction message, the server signs with the keypair (which lives in an env var), and sends the signed transaction back.

If you must sign client-side (some wallets handle this fine without @solana/web3.js's Node dependencies), consider using @solana/web3.js's tree-shakeable imports:

import { Connection, PublicKey } from '@solana/web3.js';
// avoid: import * as web3 from '@solana/web3.js'

Wildcard imports pull in the whole package, including the Node-only signing paths that trigger the crypto error. Named imports let webpack drop unused code during bundling. You should see a smaller bundle after this change — often 200KB or more knocked off.

Check your package versions before you change anything

Run npm ls @solana/web3.js. Versions before 1.75 had heavier Node dependencies. Versions 1.80+ are cleaner but still need the polyfills in the browser. There's no version that "just works" in webpack 5 without config — that's a myth I've seen repeated in Discord servers.

Don't forget Buffer in older setups

Even after fixing crypto, you might hit Buffer is not defined at runtime. That's a separate error from the same family. The ProvidePlugin entry above handles it for webpack. For Vite, nodePolyfills({ globals: { Buffer: true } }) does the job. If you're using CRA without rewiring, drop import { Buffer } from 'buffer'; at the top of the file that needs it — ugly but it works.

The pattern I see over and over: developers fix crypto, ship it, then get a Buffer error two days later because they only copied half of the polyfill config from a Stack Overflow answer. Do both at once.

Quick reference

SetupSymptomFix
Webpack 5Can't resolve 'crypto'Add resolve.fallback.crypto = require.resolve('crypto-browserify')
ViteModule 'crypto' has been externalizedInstall vite-plugin-node-polyfills and add to plugins
Next.jsSimilar webpack errorOverride webpack in next.config.js with the same fallback object
CRACan't resolve 'crypto'Use react-app-rewired with a config-overrides.js
AnywhereBuffer is not definedAdd ProvidePlugin for Buffer alongside the crypto fallback

Pick the row that matches your build tool, apply only that fix, and restart the dev server. Nine times out of ten the wall of module errors disappears. If it doesn't, the next error is usually stream or vm — add those fallbacks the same way and you'll get to a clean build.

Related Errors in Programming & Dev Tools
0X000003E9 ERROR_STACK_OVERFLOW (0X000003E9) — Real Fix for Deep Recursion ENOENT Fix npm ERR! code ENOENT in 3 steps (start with the quick one) 0X0000024E Fix ERROR_DEBUG_ATTACH_FAILED (0X0000024E) Fast curl: (35) SSL connect error Fix curl SSL connect error on macOS with self-signed certs

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.