Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@lorisleiva
Last active June 15, 2022 20:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lorisleiva/a652d42cf13cb62a0818d9fe96166a76 to your computer and use it in GitHub Desktop.
Save lorisleiva/a652d42cf13cb62a0818d9fe96166a76 to your computer and use it in GitHub Desktop.
[Metaplex] Integrate the new JS SDK with React

Integrating the Metaplex JS SDK with React apps

Create React App 4 (using Webpack 4)

Steps to install a CRA 4 app from scratch using the SDK:

# Scaffold the app.
npx create-react-app getting-started-react-cra4 --scripts-version 4.0.3
cd getting-started-react-cra4

# Install the SDK and Web3.js
npm install @metaplex-foundation/js-next @solana/web3.js

Create and log a new Metaplex instance inside src/App.js.

//..
import { Metaplex } from '@metaplex-foundation/js-next';
import { clusterApiUrl, Connection } from '@solana/web3.js';

function App() {
  const connection = new Connection(clusterApiUrl('devnet'));
  const mx = Metaplex.make(connection);
  console.log(mx);

  // ...
}

At this point, npm start will fail, complaining that it can't resolve .mjs files provided by Web3.js. To fix this we need to:

# Install react-app-rewired.
npm install react-app-rewired

# Replace "react-scripts" with "react-app-rewired" in package.json scripts.
sed -i '' 's/react-scripts /react-app-rewired /g' package.json

# Create a new file to override Webpack 4 configs.
touch config-overrides.js

Then inside that config-overrides.js file, paste the following:

module.exports = function override(webpackConfig) {
  webpackConfig.module.rules.push({
    test: /\.mjs$/,
    include: /node_modules/,
    type: "javascript/auto",
  });
  return webpackConfig;
};

Now, npm start should work. 🎉

Credits to Steven Luschen.

Even after doing this though you might have the following error when building your app for production (i.e. npm run build and serve -g build).

Uncaught TypeError: Cannot convert a BigInt value to a number

This is because Webpack will try to change the code of the deprecated nested dependency noble-ed25519 to make sure it will work on browsers that don't support BigInt. However, all modern browsers support BigInt so we can fix this by updating the browserslist object in our package.json.

  "browserslist": {
    "production": [
-     ">0.2%",
-     "not dead",
-     "not op_mini all"
+     "chrome >= 67",
+     "edge >= 79",
+     "firefox >= 68",
+     "opera >= 54",
+     "safari >= 14"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },

Now, you should be able to build and serve your application for production!

Create React App 5 (using Webpack 5)

Steps to install a CRA 5 app from scratch using the SDK:

# Scaffold the app.
npx create-react-app getting-started-react-cra5
cd getting-started-react-cra5

# Install the SDK and Web3.js
npm install @metaplex-foundation/js-next @solana/web3.js

Create and log a new Metaplex instance inside src/App.js.

//..
import { Metaplex } from '@metaplex-foundation/js-next';
import { clusterApiUrl, Connection } from '@solana/web3.js';

function App() {
  const connection = new Connection(clusterApiUrl('devnet'));
  const mx = Metaplex.make(connection);
  console.log(mx);

  // ...
}

At this point, npm start will fail, complaining that Webpack 5 does not provide polyfills that might be needed. To fix this we need to:

# Install react-app-rewired.
npm install react-app-rewired

# Install some polyfills.
npm install assert util crypto-browserify stream-browserify

# Replace "react-scripts" with "react-app-rewired" in package.json scripts.
sed -i '' 's/react-scripts /react-app-rewired /g' package.json

# Create a new file to override Webpack 4 configs.
touch config-overrides.js

Then inside that config-overrides.js file, paste the following:

const webpack = require('webpack');

module.exports = function override(webpackConfig) {
  // Disable resolving ESM paths as fully specified.
  // See: https://github.com/webpack/webpack/issues/11467#issuecomment-691873586
  webpackConfig.module.rules.push({
    test: /\.m?js/,
    resolve: {
      fullySpecified: false,
    },
  });

  // Ignore source map warnings from node_modules.
  // See: https://github.com/facebook/create-react-app/pull/11752
  webpackConfig.ignoreWarnings = [/Failed to parse source map/];

  // Polyfill Buffer.
  webpackConfig.plugins.push(
    new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
  );

  // Polyfill other modules.
  webpackConfig.resolve.fallback = {
    crypto: require.resolve("crypto-browserify"),
    stream: require.resolve("stream-browserify"),
    util: require.resolve("util"),
    assert: require.resolve("assert"),
    fs: false,
    process: false,
    path: false,
    zlib: false,
  };

  return webpackConfig;
};

Now, npm start should work. 🎉

Even after doing this though you might have the following error when building your app for production (i.e. npm run build and serve -g build).

Uncaught TypeError: Cannot convert a BigInt value to a number

This is because Webpack will try to change the code of the deprecated nested dependency noble-ed25519 to make sure it will work on browsers that don't support BigInt. However, all modern browsers support BigInt so we can fix this by updating the browserslist object in our package.json.

  "browserslist": {
    "production": [
-     ">0.2%",
-     "not dead",
-     "not op_mini all"
+     "chrome >= 67",
+     "edge >= 79",
+     "firefox >= 68",
+     "opera >= 54",
+     "safari >= 14"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },

React with Vite

Steps to install a React app with Vite from scratch using the SDK:

# Scaffold the app.
npm create vite@latest getting-started-vite -- --template react
cd getting-started-vite
npm install

# Install the SDK and Web3.js
npm install @metaplex-foundation/js-next @solana/web3.js

Create and log a new Metaplex instance inside src/App.jsx.

//..
import { Metaplex } from '@metaplex-foundation/js-next';
import { clusterApiUrl, Connection } from '@solana/web3.js';

function App() {
  const connection = new Connection(clusterApiUrl('devnet'));
  const mx = Metaplex.make(connection);
  console.log(mx);

  // ...
}

Add the following script in your index.html file.

  <body>
    <div id="root"></div>
+   <script>
+     // Global node polyfill.
+     window.global = window;
+   </script>
    <script type="module" src="/src/main.jsx"></script>
  </body>

Install the following polyfills and plugins.

npm install -D assert util crypto-browserify @esbuild-plugins/node-globals-polyfill rollup-plugin-node-polyfills

Replace the content of your vite.config.js file with the following to provide polyfills for both development and production.

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
import nodePolyfills from 'rollup-plugin-node-polyfills';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      stream: 'rollup-plugin-node-polyfills/polyfills/stream',
      events: 'rollup-plugin-node-polyfills/polyfills/events',
      assert: 'assert',
      crypto: 'crypto-browserify',
      util: 'util',
    },
  },
  define: {
    'process.env': process.env ?? {},
  },
  build: {
    target: 'esnext',
    rollupOptions: {
      plugins: [
        nodePolyfills({ crypto: true }),
      ],
    },
  },
  optimizeDeps: {
    esbuildOptions: {
      plugins: [
        NodeGlobalsPolyfillPlugin({ buffer: true }),
      ],
    }
  },
});

And that's it! Now you can run npm run dev when developing and npm run build for production.

@MNaguib2611
Copy link

Hello Lori,
I followed your steps for react webpack5
and I recieved this error :

Task.ts:58 Uncaught (in promise) TypeError: abort_controller__WEBPACK_IMPORTED_MODULE_0__.AbortController is not a constructor
at Task.forceRun (Task.ts:58:1)
at Task.run (Task.ts:47:1)
at OperationClient.execute (OperationClient.ts:76:1)
at NftClient.findByMint (NftClient.ts:35:1)
at getNftMetaData (dashboard.js:38:1)
at Dashboard (dashboard.js:44:1)
at renderWithHooks (react-dom.development.js:16305:1)
at mountIndeterminateComponent (react-dom.development.js:20145:1)
at beginWork (react-dom.development.js:21587:1)
at beginWork$1 (react-dom.development.js:27426:1)

I would really appreciate your help

@lorisleiva
Copy link
Author

Hey @MNaguib2611 👋 Yeah that was a bug on version 0.11.0 sorry about that. Try version 0.11.1, it should work. 🙂

@MNaguib2611
Copy link

Thank you for the very quick response
my issue was resolved
I appreciate your help

This file is a life saver btw
I truly appreciate your work

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