MacBobby Chibuzor Go, Solidity, and Haskell developer interested in the cloud native world and blockchain technology. A fanatic for technical writing and open source contribution.

Full-stack DApp tutorial with Vite + React, Tailwind CSS, and Solidity

6 min read 1911

Logos Over a Red Background

In this tutorial, we’ll demonstrate how to build a full-stack DApp, test it on the Ropsten testnet, and deploy it with the Alchemy Web3 development platform. We’ll use Vite, React, and Tailwind CSS to build the DApp frontend and Solidity to create the backend.

Jump ahead:

Prerequisites

To follow along with this article’s demo, you’ll need the following:

  • Working knowledge of building frontend UIs with React and CSS
  • Familiarity with Tailwind CSS, Vite, VS Code, and npm
  • Basic understanding of the Solidity language syntax
  • Familiarity with the Ethereum ecosystem

Additionally, it will be helpful to have the following VS Code extensions:

Getting started

We’ll begin by creating a folder with the name of our intended project. Then, we’ll open the folder in VS Code. You may use the code editor of your choosing.

Now, we’ll create two folders, client and smart_contract, and then navigate to the client folder on the terminal.

Scaffolding the project

We’ll create a scaffold for our React application using Vite. This aptly named build tool (Vite is French for quick) helps developers scaffold a web project quickly and easily.

Install Vite with npm, like so:

$ npm create [email protected]

Next, run the following command to initialize a scaffold for the project and allow the installation of [email protected]:

$ npm init [email protected]

When the scaffold creation is complete, you’ll see output in your terminal requesting project details.

Choose a project name and then a package name. This demo uses web3-world.com for both the project and package names.

Next, select React for both the framework and the variant.

You should see the following on the screen:

npm Create Vite

This creates the following files for the project:

  • index.html
  • package.json
  • src folder
  • vite.config.js

Next, run the following commands:

$ npm install
$ npm run dev

This code will deploy the project as a React application accessible on the specified local network. If we Navigate to the port on localhost, we should see the following:

Create React App

We can stop the terminal from hosting the server with the Ctrl + C command.

Next, we’ll navigate into the smart_contract directory and run the following:

$ npm init -y

This creates a package.json file for our smart_contract folder.

Now, we’ll use npm to install Tailwind CSS. We’ll add Tailwind to our React project with create-react-app. To do this, we’ll simply follow the four steps outlined in the Tailwind documentation.

Then, we’ll install Tailwind in the client folder, like so:

$ npm install -D tailwindcss postcss autoprefixer
$ npx tailwindcss init -p

After running these commands, we can confirm that two files were added to our project folder: a Tailwind CSS config file and a PostCSS config file.

Now, we’ll delete the contents of the project’s tailwind.config.js file and replace them with the contents from the tailwind.config file on the documentation page:

module.exports = {

 content: [

   "./src/**/*.{js,jsx,ts,tsx}",

 ],

 theme: {

   extend: {},

 },

 plugins: [],

}

Next, we’ll replace the contents of the copy of the project’s index.css file with the directives shown on the documentation page:

@tailwind base;
@tailwind components;
@tailwind utilities;

Building the DApp UI

Now, let’s work on building our application’s frontend.

We’ll start by creating a new folder, called components, inside the src folder. There, we’ll create the dashboard components for our project:

  • Navigation_bar.jsx
  • Footer.jsx
  • Loader.jsx
  • Services.jsx
  • Welcome.jsx
  • Transactions.jsx

These components will each contain the following code, replacing Component_name with their respective names:

const Component_name = () => {
    return (
    <h1>Component_name</h1>
    );
}

export default Component_name;

We’ll use the following code to create an index.js file to export all these components:

export { default as Loader } from './Loader';
export { default as Navbar } from './Navbar';
export { default as Footer } from './Footer';
export { default as Welcome } from './Welcome';
export { default as Services } from './Services';
export { default as Transactions } from './Transactions';

Next, we’ll focus on working on the client UI. We’ll primarily be dealing with these three files and folders for now:

  • index.css: contains our desired frontend styling
  • images folder: this is located under the client folder and contains our desired images
  • componentsfolder: contains our dashboard elements, like the ones listed above

Now, we’ll install the react-icons and ethers packages. The ethers package will make it possible for us to interact with the smart contract.



$ npm install react-icons ethers

If we run the application following installation, we should see the new changes.

Building the smart contract using Hardhat

Hardhat is an Ethereum development environment that is used to run Solidity code locally.

Run the following command in the smart_contracts folder to install Hardhat on your local machine:

npm install --save-dev hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers

This command installs the dependencies needed to use the Hardhat development ecosystem. You’ll notice that there are new folders and files in the codebase. To test that these are working perfectly, run this command:

npx hardhat test

This should compile the Solidity source file now available in the contracts folder in the main project directory. It will deploy the Solidity file and test to see if it runs successfully.

To build the backend for our DApp, we’ll add our Solidity code to the contracts folder and delete the Greeter.sol file.

Next, we’ll create a Transactions.sol file and input the following code:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

// contract name is conventionally the same as file name
contract Transactions {
    //transactionCounter holds the number of our transactions
    uint256 transactionCounter;
    // we  will call the event later on
    event Transfer(address from, address receiver, uint amount, string message, uint256 timestamp, string keyword);

    // struct will hold all above object properties
    struct TransferStructure {
        address sender;
        address receiver;
        uint amount;
        string message;
        uint256 timestamp;
        string keyword;
    }

    // create an array to store transactions with above objects as fields
    TransferStructure[] transactions;

    function addToBlockchain(address payable receiver, uint amount, string memory message, string memory keyword) public {
        transactionCounter += 1;
        transactions.push(TransferStructure(msg.sender, receiver, amount, message, block.timestamp, keyword));

        emit Transfer(msg.sender, receiver, amount, message, block.timestamp, keyword);
    }

    function getAllTransactions() public view returns (TransferStructure[] memory) {
        return transactions;
    }

    function getTransactionCount() public view returns (uint256) {
        return transactionCounter;
    }
}

At this point, the entire backend is complete!

Now, we can move on to testing and then deployment. In the scripts folder, there is a sample-script.js that we can convert to a deploy-script.js script.

We’ll use the following for the content of the deploy-script.js script:

const main = async () => {
  
  // We get the contract to deploy
  const Transactions = await hre.ethers.getContractFactory("Transactions");
  const transactions = await Transactions.deploy();

  await transactions.deployed();

  console.log("Transactions deployed to:", transactions.address);
}

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
}

runMain(); 

You should have a project structure similar to this:

Project Structure

Testing the smart contract with the Ropsten testnet

The project build is now complete and the deployment script is ready for execution. However, deploying smart contracts on the Ethereum blockchain warrants the use of a computational resource called gas. Gas must be purchased with ether, and the price can fluctuate. Depending on the power of the smart contract, the price could be a little or it could be a lot.

Fortunately, Ethereum allows the use of test ether (fake ether) that can be used to test smart contracts on test networks. To take advantage of this, we’ll create an Ethereum wallet with MetaMask. Then, we’ll request test ether for our deployment.

After creating, or logging into, your Ethereum wallet on metamask.io, go to Show Test Networks under Settings and select Ropsten Account. Next, copy the Ropsten address and navigate to faucet.egorfine.com to get the testnet faucet.

Once you receive the test eth to your address, we’ll move on to deployment.

Deployment with Alchemy

We’ll deploy our smart contract with the Alchemy Web3 development platform.

From alchemy.com, we’ll click on Get started for free and register for an account. Next, we’ll select the Ethereum + L2 option and then click Get Started.

This will bring us to a dashboard where we can specify the requested credentials:

Alchemy Page

From the dashboard Network dropdown, we’ll select Ropsten. if you prefer, you can select the Kovan or Rinkeby networks instead; both permit requests for test eth.

Next, we’ll be asked to choose a plan for deployment. Because this is a demo, we’ll select the FREE FOREVER plan at $0, Skip For Now for payment details and Capped Capacity for scaling policy. Next, click Continue.

In the dashboard, we’ll click the +Create App button. Then, we’ll add a description for the app. We’re deploying on the Ropsten test network now, but we can deploy this same contract on the Mainnet (using main eth) later.

Click the View Key button and copy the HTTP key. Now, we’ll go back to our coding environment and paste the key in the hardhat.config.js file in the smart_contract folder of our codebase.

We’ll format the file to contain the following code:

require("@nomiclabs/hardhat-waffle");

module.exports = {
  solidity: "0.8.0",
  networks: {
    ropsten: {
      url: 'https://eth-ropsten.alchemyapi.io/v2/HwDxJjZs10sSafsRoYKKqQ0Db1Yaexhv',
      accounts: '0xPrivateKeyToYourAccount' // replace with your private key
    }
  }
};

To deploy the entire contract, we’ll navigate to our smart_contract folder in the terminal and run the following command:

npx hardhat run scripts/deploy.js --network ropsten

This will compile the code and deploy it to a contract address. That contract address will be specified in the terminal, and a new Transactions.json file will be created in the contracts folder.

Next, we’ll make a utils folder and then create the following files in it:

  • constants.js
  • Transactions.json, containing the contents of Transactions.json in the contracts folder
  • dummy data in a random JavaScript file (dummyData.js, for example)

We’ll import the Application Binary Interface (ABI) from the Transactions.json file into the constants.js file, like so:

import abi from "./Transactions.json";

export const contractAddress = "0xTheGeneratedContractAddress";
export const contractABI = abi.abi;

A context folder in the client/src directory will need to be connected to the blockchain. Inside the folder, we’ll write a TransactionContext.jsx file that will import the following:

import React, { useEffect, useState } from "react";
import { ethers } from "ethers";
import { contractABI, contractAddress } from "../utils/constants";

export const TransactionContext = React.createContext();

It will also contain the Ethereum window object that will allow us manage the connection to the Ethereum blockchain.:

const { ethereum } = window;

That’s it! We’ve built, tested, and deployed our DApp!

Conclusion

In this tutorial, we demonstrated a step-by-step process for building a full-stack DApp that could serve as an NFT marketplace or an exchange platform for trading Ethereum. We showed how to structure a full-stack DApp project and detailed the requirements and process for building, testing, and deploying a Solidity smart contract.

I hope you enjoyed this tutorial. To further enhance your Web3 development skills, try building this project by yourself using this article as a guide.

LogRocket: Full visibility into your production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket combines session replay, product analytics, and error tracking – empowering software teams to create the ideal web and mobile product experience. What does that mean for you?

Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay problems as if they happened in your own browser to quickly understand what went wrong.

No more noisy alerting. Smart error tracking lets you triage and categorize issues, then learns from this. Get notified of impactful user issues, not false positives. Less alerts, way more useful signal.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Join organizations like Bitso and Coinsquare who use LogRocket to proactively monitor their Web3 apps

Client-side issues that impact users’ ability to activate and transact in your apps can drastically affect your bottom line. If you’re interested in monitoring UX issues, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps — .

MacBobby Chibuzor Go, Solidity, and Haskell developer interested in the cloud native world and blockchain technology. A fanatic for technical writing and open source contribution.

2 Replies to “Full-stack DApp tutorial with Vite + React, Tailwind CSS,…”

  1. Have you seen moralis.io ? Speeds up web3 dev like crazy😅
    : Please check it out, it makes Dapps dev smoother and faster

    So we’ll just be needing => {

    React
    Solidity
    Moralis
    Remix
    }

    Nice content, I love the way you simplified your solidity though.
    This is my first time here👌

  2. Incomplete article. Footer.jsx, Loader.jsx, Navigation_bar.sx not being used anywhere. Neither is the transactionContext. Nor is the ABI.
    There is no interaction between the frontend and the smart contract.
    It looks like the article got truncated. Please fix it.

Leave a Reply