In this comprehensive guide, you’ll learn how to build a full-stack decentralized application (dApp) using React, Ethers.js, Solidity, and Hardhat. Whether you're a beginner or an experienced developer, this tutorial will walk you through the entire process—from setting up your development environment to deploying and interacting with smart contracts.
Why This Stack Matters
Ethereum development involves multiple layers, each requiring specific tools and knowledge. While individual tools have decent documentation, there's often a gap in understanding how they all work together. This guide bridges that gap by providing a practical, end-to-end walkthrough using modern libraries and frameworks.
The stack we’ll use includes:
- React for the frontend
- Hardhat for Ethereum development and testing
- Ethers.js for blockchain interactions
- MetaMask for wallet management
We won’t cover The Graph Protocol in this tutorial, but it’s a powerful tool for querying blockchain data efficiently and will be explored in future guides.
Core Technologies Explained
1. Hardhat: Ethereum Development Environment
Hardhat is a development environment that lets you compile, deploy, test, and debug Ethereum smart contracts. It simplifies working with Solidity code and supports local, testnet, and mainnet deployments.
Alternatives like Ganache and Truffle exist, but Hardhat offers excellent flexibility and integration with modern tools.
2. Ethers.js: Ethereum Web Client Library
Ethers.js is a compact, complete library for interacting with the Ethereum blockchain from client-side applications. It handles tasks like reading contract data, sending transactions, and managing wallets.
Web3.js is another popular option, but Ethers.js is often preferred for its simplicity and smaller bundle size.
3. MetaMask: Wallet and Connection Management
MetaMask is a browser extension that manages user accounts and connects them to the blockchain. It injects a global ethereum API into websites, allowing apps to request transactions and signatures securely.
4. React: Frontend Framework
React is a JavaScript library for building user interfaces. Its component-based architecture and vast ecosystem make it ideal for dApp frontends. Frameworks like Next.js or Gatsby can further enhance your project.
What We’ll Build
You’ll create two smart contracts and a React frontend:
- Greeting Contract: Stores and updates a message on the Ethereum blockchain.
- Token Contract: Mints tokens, allows transfers, and reads balances.
The React app will enable users to:
- Read and update the greeting from the contract
- Send tokens to other addresses
- Check token balances
👉 Explore step-by-step deployment guide
Prerequisites
Before starting, ensure you have:
- Node.js installed on your machine
- MetaMask installed in your browser
No real Ethereum is needed—we’ll use test networks and fake ETH throughout this tutorial.
Setting Up the Development Environment
Step 1: Initialize the Project
Create a new project directory and initialize it with npm:
mkdir ethereum-dapp-tutorial
cd ethereum-dapp-tutorial
npm init -yStep 2: Install Hardhat
Install Hardhat as a development dependency:
npm install --save-dev hardhatRun npx hardhat to create a sample project. Select the default options to generate a basic structure.
Step 3: Install Ethers and React Dependencies
Install Ethers.js for blockchain interactions and React for the frontend:
npm install ethers
npx create-react-app client
cd client
npm startWriting and Deploying Smart Contracts
Create the Greeting Contract
In the contracts folder, create Greeting.sol:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract Greeting {
string private greeting;
constructor(string memory _greeting) {
greeting = _greeting;
}
function getGreeting() public view returns (string memory) {
return greeting;
}
function setGreeting(string memory _greeting) public {
greeting = _greeting;
}
}Deploy the Contract
Create a deployment script in scripts/deploy.js:
async function main() {
const Greeting = await ethers.getContractFactory("Greeting");
const greeting = await Greeting.deploy("Hello, Ethereum!");
await greeting.deployed();
console.log("Greeting contract deployed to:", greeting.address);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});Run npx hardhat run scripts/deploy.js --network localhost to deploy locally.
Building the React Frontend
Connect to the Contract
In your React app, use Ethers.js to connect to the deployed contract:
import { ethers } from 'ethers';
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contractAddress = 'YOUR_CONTRACT_ADDRESS';
const abi = [ /* ABI here */ ];
const contract = new ethers.Contract(contractAddress, abi, signer);Read and Update the Greeting
Add functions to read and write data:
const getGreeting = async () => {
const greeting = await contract.getGreeting();
setGreeting(greeting);
};
const updateGreeting = async () => {
const tx = await contract.setGreeting('New greeting!');
await tx.wait();
getGreeting();
};Testing and Deployment
Run Tests
Write tests in Hardhat to verify contract functionality:
describe("Greeting", function () {
it("Should return the correct greeting", async function () {
const Greeting = await ethers.getContractFactory("Greeting");
const greeting = await Greeting.deploy("Hello, world!");
await greeting.deployed();
expect(await greeting.getGreeting()).to.equal("Hello, world!");
});
});Run tests with npx hardhat test.
Deploy to Testnet
Configure Hardhat to use a testnet like Rinkeby or Goerli. Update hardhat.config.js with network details and deploy using:
npx hardhat run scripts/deploy.js --network goerliFrequently Asked Questions
What is the difference between Ethers.js and Web3.js?
Ethers.js and Web3.js are both libraries for interacting with Ethereum. Ethers.js is lighter and designed for client-side use, while Web3.js offers more features but has a larger bundle size. For most dApps, Ethers.js is sufficient.
Do I need real ETH to deploy contracts?
No. You can use testnets like Goerli or Sepolia, which provide free test ETH. Use faucets to obtain test ETH for deployment.
How do I handle gas fees in development?
Hardhat and other development environments simulate gas fees locally. On testnets, gas fees are paid with test ETH, which has no real value.
Can I use this stack with other blockchains?
Yes. Hardhat and Ethers.js support Ethereum-compatible blockchains like Polygon, Binance Smart Chain, and Avalanche. Adjust the network configuration in Hardhat to deploy elsewhere.
What are the security best practices for smart contracts?
Always audit your code, use established libraries like OpenZeppelin, and test thoroughly. Tools like Slither or Mythril can help identify vulnerabilities.
How do I update a deployed contract?
Smart contracts are immutable by default. To update logic, use proxy patterns or deploy a new version. Consider this carefully during design.
Next Steps
Now that you’ve built a basic dApp, explore advanced topics like:
- Integrating The Graph for indexed queries
- Adding user authentication with Web3Modal
- Implementing tokenomics in your token contract
- Optimizing gas usage and contract efficiency
👉 Discover advanced Ethereum development strategies
Remember, practice is key. Experiment with modifications, deploy often, and engage with the developer community to deepen your understanding. Happy building!