Step-by-Step Guide to React Frontend Development and Smart Contract Connection

Welcome back to our series on building a decentralized voting application (DApp) using the Ethereum network! In Article 3 , we tested and deployed the smart contract to the Sepolia testnet. Now it’s time to bring the DApp to life by developing the frontend using React and connecting it to the smart contract via Ethers.js .
In this article, we’ll:
Explore the structure of the React app.
Understand how Ethers.js connects the frontend to the smart contract.
Highlight key components like creating elections, registering candidates, and casting votes.
Let’s dive in!
The Structure of the React App
The React app is the user interface (UI) of our voting DApp. It allows users to interact with the smart contract seamlessly. Here’s an overview of the key components:
Navigation : A top bar that shows the connected wallet address and provides navigation links.
Home : The landing page where users can see their roles (owner, voter, etc.) and access different features.
Create Election : Allows the owner to create new elections.
Register Candidate : Lets users register candidates for specific elections.
Register Voter : Enables users to register themselves as voters.
Elections List : Displays all active and past elections.
Vote : Allows registered voters to cast their votes.
Results : Shows the results of completed elections.
Each of these components interacts with the smart contract using Ethers.js , which acts as the bridge between the frontend and the blockchain.
Connecting the Frontend to the Smart Contract with Ethers.js
Before diving into the code, let’s understand how Ethers.js works:
Provider : Connects to the Ethereum network (e.g., MetaMask).
Signer : Represents the user’s wallet and allows signing transactions.
Contract : Interacts with the deployed smart contract using its ABI (Application Binary Interface) and address.
Here’s how we set up the connection in the useEffect
hook of App.jsx
:
useEffect(() => {
const init = async () => {
try {
// Check if MetaMask is installed
if (window.ethereum) {
// Request account access
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
const account = accounts[0];
setAccount(account);
// Create provider and signer
const provider = new BrowserProvider(window.ethereum);
setProvider(provider);
const signer = await provider.getSigner();
setSigner(signer);
// Create contract instance
const contract = new ethers.Contract(contractAddress, VotingSystemABI.abi, signer);
setContract(contract);
// Check if the user is the owner or a registered voter
const owner = await contract.owner();
setIsOwner(owner.toLowerCase() === account.toLowerCase());
const isRegistered = await contract.isVoterRegistered(account);
setIsVoterRegistered(isRegistered);
setLoading(false);
} else {
alert("Please install MetaMask or another Ethereum wallet to use this dApp!");
}
} catch (error) {
console.error("Error initializing app:", error);
setLoading(false);
}
};
init();
}, []);
This setup ensures that:
The app connects to the user’s wallet (MetaMask).
The contract instance is created and ready for interaction.
The app checks whether the user is the owner or a registered voter.
Breaking Down App.jsx
The App.jsx
file serves as the central hub of the DApp. Let’s break it down into meaningful sections:
State Management
The app uses React’s useState
to manage key states:
account
: Stores the connected wallet address.provider
andsigner
: Handle blockchain interactions.contract
: Represents the deployed smart contract.isOwner
andisVoterRegistered
: Determine the user’s role.currentPage
: Tracks the current page being displayed.selectedElection
: Stores details of the selected election.
Navigation and Page Rendering
The navigateTo
function allows users to switch between pages:
const navigateTo = (page, election = null) => {
setCurrentPage(page);
if (election) {
setSelectedElection(election);
}
};
The renderPage
function dynamically renders the appropriate component based on currentPage
:
const renderPage = () => {
if (loading) {
return <div className="loading">Loading dApp...</div>;
}
switch (currentPage) {
case 'home':
return <Home navigateTo={navigateTo} isOwner={isOwner} isVoterRegistered={isVoterRegistered} />;
case 'createElection':
return <CreateElection contract={contract} account={account} navigateTo={navigateTo} />;
case 'registerCandidate':
return <RegisterCandidate contract={contract} account={account} navigateTo={navigateTo} />;
case 'registerVoter':
return <RegisterVoter contract={contract} account={account} navigateTo={navigateTo} setIsVoterRegistered={setIsVoterRegistered} />;
case 'elections':
return <Elections contract={contract} account={account} navigateTo={navigateTo} isOwner={isOwner} />;
case 'vote':
return <Vote contract={contract} account={account} navigateTo={navigateTo} election={selectedElection} />;
case 'results':
return <Results contract={contract} account={account} navigateTo={navigateTo} election={selectedElection} />;
default:
return <Home navigateTo={navigateTo} isOwner={isOwner} isVoterRegistered={isVoterRegistered} />;
}
};
Handling Account Changes
The app listens for changes in the connected wallet:
window.ethereum.on('accountsChanged', function (accounts) {
setAccount(accounts[0]);
window.location.reload();
});
This ensures the app updates when the user switches accounts in MetaMask.
Key Components
Let’s briefly explore some of the key components:
Home
The Home
component serves as the landing page. It displays:
Whether the user is the owner or a registered voter.
Navigation links to create elections, register candidates, or view elections.
Create Election
The CreateElection
component allows the owner to create new elections. It calls the createElection
function from the smart contract:
await contract.createElection(name, description, startTime, endTime);
Register Candidate
The RegisterCandidate
component lets users register candidates for specific elections. It calls the registerCandidate
function:
await contract.registerCandidate(electionId, name, partyAffiliation, personalInfo);
Vote
The Vote
component allows registered voters to cast their votes. It ensures the user hasn’t already voted and calls the castVote
function:
await contract.castVote(electionId, candidateId);
Results
The Results
component fetches and displays the winner of a completed election. It calls the getWinner
function:
const winnerId = await contract.getWinner(electionId);
Running the Frontend
To run the frontend locally:
Install dependencies:
npm install
Start the development server:
npm run dev
Make sure your MetaMask wallet is connected to the Sepolia testnet and has test ETH for gas fees.
Wrapping Up
Congratulations—you now have a fully functional React frontend for your voting DApp! This is a major step toward completing your decentralized voting system.
Your Task : Try running the frontend locally and interacting with the smart contract. Share your experience in the comments. Did you encounter any challenges? How did you resolve them?
Stay tuned for Article 5 , where we’ll conclude the series with best practices and tips for deploying and maintaining your DApp. See you there! 🚀
Subscribe to my newsletter
Read articles from chainyblock directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

chainyblock
chainyblock
👋 Hi, We are ChainyBlock, a passionate advocate for blockchain technology and its transformative potential. With a background in software engineering and cybersecurity, We've spent a lot of time exploring how decentralized systems can reshape industries, foster trust, and create a more inclusive future. 🎯 What Drives Me? I believe that understanding complex technologies like blockchain shouldn’t be reserved for experts—it should be accessible to everyone. That’s why I’m here: to break down the fundamentals of Web3, cryptocurrencies, smart contracts, and decentralized applications into simple, actionable insights. Whether you’re a beginner or a seasoned learner, my goal is to help you navigate this rapidly evolving space with confidence. 💭 Dreams for the Future: I dream of a world where blockchain technology enables secure, transparent, and efficient systems for everyone—regardless of location or background. Through education and collaboration, I hope to inspire others to embrace the possibilities of Web3 and contribute to this global movement. 🌟 Let’s Connect: Feel free to reach out if you’d like to discuss blockchain innovations, collaborate on projects, or share ideas. Together, we can build a smarter, decentralized future. 🌐💡