Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save wirednkod/b59b8ecefe32cbde47a015b0f2d619e1 to your computer and use it in GitHub Desktop.
Save wirednkod/b59b8ecefe32cbde47a015b0f2d619e1 to your computer and use it in GitHub Desktop.
How to create a non-CRA React project with Webpack and substrate-connect

Intro

This repo is meant as a step-by-step guide on how to setup a React project (non CRA) with Webpack (v5 with Babel), and @substrate/connect (light client in-browser) in order to kick-off your first dApp. (Note: This repo is on purpose built without CRA and it is meant as a guideline for devs who wants to start their dApp without CRA but with Webpack. There are two different repos following the installation with Parcel (v2) and pending CRA.)

To learn more and understand what is a light-client - here is a helpful article.

Before starting with the installation steps which will include explanation of what is what, first of all a small intro on each main component of this dApp:

Main components

  • React: Main Javascript library for building user Interfaces;
  • Webpack (v5): An open-source JavaScript module bundler;
  • Babel: a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments;
  • Substrate connect (npm): Prototype for using @polkadot/api with the smoldot WASM light client either by passing chainspecs or using the extension by predefined chains.

Project structure

First things first - we need a structure of the project. Navigate to the directory that you want everything to be placed and run:

$ mkdir react-webpack-substrateconnect

and enter that newly created directory:

$ cd react-webpack-substrateconnect

Note: All commands that appear from now on, need to run in your root directory

Then in order to iniate the project you should run:

$ yarn init

which will ask some questions in order to setup your project. e.g:

  name (testdir): my-awesome-package
  version (1.0.0):
  description: The best package you will ever find.
  entry point (index.js):
  git repository: 
  author: Yarn 
  license (MIT):
  private:

In order to make that process faster a -y can be added that (yarn init -y) will five some default answers to the questions. This process will result to a new file package.json

Open the file with your favourite editor and add the following:

  "scripts": {
    "start": "webpack server --config webpack.config.js",
    "build": "webpack"
  },

As names denote, yarn start will initiate your application at port 3000, while yarn build will bundle your app inside a dist directory.

Rest of the directory structure that we will use can be created with the following command:

$ mkdir public public/assets src

Create main html file:

Run the following command: $ touch public/index.html Open create file with your favourite editor and place inside the following code:

  <!DOCTYPE html>
  <html>
    <head>
      <title>Webpack / substrate-connect sample</title>
    </head>
    <body>
      <div id="root">
      <script src="./bundle.js"></script>
    </body>
  </html>

Setup Webpack (v5 - w Babel) - dependencies and configuration)

When it comes to Webpack module bundler there is some configuration that needs to take place and we need to install quite a few dependencies. These dependencies are required by Webpack so it can detect and work with the various file types.

The following command will install all the dependencies needed for webpack as devDependencies:

$ yarn add -D webpack webpack-cli webpack-dev-server @babel/core @babel/preset-env @babel/preset-react babel-loader style-loader css-loader html-webpack-plugin

These are explained below:

  • webpack: Installs the webpack module bundler;
  • webpack-cli: Offers a variety of commands that make it easier to work with webpack on the command line;
  • webpack-dev-server: Allows us to use a simple web server with hot reload;
  • @babel/core: Core package for the Babel compiler;
  • @babel/preset-env: A preset that allows to use latest JavaScript syntax;
  • @babel/preset-react: Babel transpiler of React code to plain JavaScript;
  • babel-loader: Plugin that enables Webpack to work with Babel and its presets;
  • style-loader: ;
  • css-loader: ;
  • html-webpack-plugin: Plugin that generates an HTML file, including all Webpack bundles via script tags.

After the command is finished installing the dependencies, we need to create a webapcak configuration file and a babel config file that will setup the presets installed before.

Webpack configuration file

Run the following command: $ touch webpack.config.js

Open that file with your favourite editor and place inside the following:

  const path = require('path');
  const webpack = require('webpack');
  const HtmlWebpackPlugin = require("html-webpack-plugin");

  module.exports = {
    mode: 'development',
    entry: './src/index.js',
    devtool: 'inline-source-map',
    devServer: {
      contentBase: path.join(__dirname, './'), // where dev server will look for static files, not compiled
      publicPath: '/', //relative path to output path where  devserver will look for compiled files
      port: 3000, // port that dev server will run
      hot: true
    },
    output: {
      path: path.resolve(__dirname, 'dist'),
      publicPath: '/',
      filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader", // Babel Configuration
                    options: {
                        presets: [
                            "@babel/preset-env",
                            "@babel/preset-react"
                        ]
                    }
                }
            },
            {
              test: /\.css$/i,
              use: ["style-loader", "css-loader"]
            }
        ]
    },
    resolve: {
      extensions: [".js", ".jsx"],
      fallback: {
          "crypto": require.resolve("crypto-browserify"),
          "stream": require.resolve("stream-browserify")
      }
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./public/index.html"
        }),
        new webpack.ProvidePlugin({
          Buffer: ['buffer', 'Buffer'],
        }),
        new webpack.HotModuleReplacementPlugin()
    ]
  };

Beside the most "known" parts that can are explained in Webpack's configuration page there are 2 very specific parts that I would like to explain the reason behind them:

1) crypto-browserify and steam-browserify

The following fallbacks are inside the "resolve" sections of webpack.config.js:

    fallback: {
        "crypto": require.resolve("crypto-browserify"),
        "stream": require.resolve("stream-browserify")
    }
  • crypto-browserify: reimplement node's crypto module, in pure javascript so that it can run in the browser.
  • stream-browserify: This module uses readable-stream, with additions for compatibility with npm packages that use old Node.js stream APIs.

These 2 are needed in the webpack configuration in order to allow the React code "understand" the substrate-connect and smoldot npm packages that are imported and needed for the React code to use it.

2) Provide Plugin Buffer

Webpack 5 has removed Buffer from the configuration schema and uses false as default. As a result we need to add the following in the plugins for Webpack to use it correctly.

new webpack.ProvidePlugin({
  Buffer: ['buffer', 'Buffer'],
}),

Add substrate-connect dependency

Run the command:

$ yarn add @substrate/connect;

Add react and react-dom dependencies

Run the command:

$ yarn add react react-dom

Write your code

Inside directory src add index.js:

import "regenerator-runtime/runtime";

import React, { UseState } from "react";
import ReactDom from "react-dom";
import "./index.css";
import App from './App';

ReactDom.render(<App />, document.getElementById("root"));

Notice the import "regenerator-runtime/runtime";

This is needed in order to resolve an issue that webpack has handling async/await

For rest of the implementation you can see some sample code here or go on and create your own.

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