Developing a decentralized application (dApp) is one of the most exciting ways to enter the world of Web3. Unlike traditional apps that rely on centralized servers, dApps operate on blockchain networks, leveraging smart contracts and decentralized infrastructure to offer enhanced security, transparency, and resistance to censorship. In this guide, you'll learn how to build an end-to-end dApp that fetches and stores the current price of ETH using Chainlink's price feeds.
By the end of this tutorial, you’ll have a fully functional dApp that interacts with a smart contract deployed on the Ethereum testnet—no prior blockchain experience required.
👉 Discover how to turn blockchain ideas into real-world dApps with powerful tools and resources.
What Is a dApp?
A decentralized application (dApp) runs its backend logic on a blockchain network instead of a centralized server. While the frontend can be built using standard web technologies like HTML, CSS, and JavaScript, the core business logic resides in smart contracts—self-executing programs deployed on the blockchain.
This architecture brings several advantages over traditional Web2 applications:
- No downtime: Once deployed, dApps run continuously without relying on a single point of failure.
- Censorship resistance: No central authority can shut down or alter the application.
- Trustless execution: Logic is executed transparently and cannot be tampered with.
- Enhanced privacy: Users interact directly through wallets without revealing personal data.
However, there are trade-offs: limited scalability, higher transaction costs, and a steeper learning curve for users unfamiliar with Web3 wallets and gas fees.
Core Components of a dApp
Every dApp consists of three main components:
Smart Contracts
Smart contracts contain the core logic and state of your application. They are immutable once deployed and run on the blockchain, ensuring trustless and tamper-proof operations. In our example, the smart contract will fetch and store ETH/USD price data from Chainlink’s decentralized oracle network.
Frontend (UI)
The user interface is typically built using familiar frameworks like React, Vue, or Angular. It communicates with the smart contract via libraries such as ethers.js or web3.js, enabling users to interact with the blockchain through their Web3 wallet—like MetaMask.
Data Storage
Storing large amounts of data directly on-chain is expensive and inefficient. Most dApps use off-chain solutions like IPFS or Filecoin for storing files, while keeping only critical state changes and references on the blockchain.
Step 1: Write the Smart Contract
Our dApp will use Solidity to create a smart contract that retrieves the latest ETH/USD price from Chainlink’s data feed and stores it permanently.
We’ll use Hardhat, a popular Ethereum development environment, to write, test, and deploy our contract.
First, set up your project:
mkdir chainlink-dapp-example
cd chainlink-dapp-example
mkdir backend
cd backend
npm init -y
npm install --save-dev hardhat
npx hardhatSelect "Create a JavaScript project" and accept default settings.
Now, delete the default Lock.sol file in the contracts folder and create a new file called PriceConsumerV3.sol. Paste the following code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PriceConsumerV3 {
AggregatorV3Interface internal priceFeed;
int public storedPrice;
constructor() {
priceFeed = AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e); // Rinkeby ETH/USD feed
}
function getLatestPrice() public view returns (int) {
(
/*uint80 roundID*/,
int price,
/*uint startedAt*/,
/*uint timeStamp*/,
/*uint80 answeredInRound*/
) = priceFeed.latestRoundData();
return price;
}
function storeLatestPrice() external {
storedPrice = getLatestPrice();
}
}This contract connects to Chainlink’s ETH/USD price feed on the Rinkeby testnet and allows anyone to update the stored price by calling storeLatestPrice().
Step 2: Deploy the Contract
Before deploying, install necessary dependencies:
npm install --save-dev @nomicfoundation/hardhat-toolbox
npm install @chainlink/contracts --save
npm install dotenvConfigure Hardhat by updating hardhat.config.js:
require("@nomicfoundation/hardhat-toolbox");
require('dotenv').config();
const RINKEBY_RPC_URL = process.env.RINKEBY_RPC_URL || "https://eth-rinkeby.alchemyapi.io/v2/your-api-key"
const PRIVATE_KEY = process.env.PRIVATE_KEY || "abcdef"
module.exports = {
defaultNetwork: "rinkeby",
networks: {
rinkeby: {
url: RINKEBY_RPC_URL,
accounts: [PRIVATE_KEY],
saveDeployments: true,
},
},
solidity: "0.8.9",
};Create a .env file in the backend folder:
RINKEBY_RPC_URL=your_infura_or_alchemy_url
PRIVATE_KEY=your_wallet_private_key🔐 Security Tip: Never use your mainnet wallet private key. Create a new MetaMask account for testing.
Update scripts/deploy.js:
const hre = require("hardhat");
async function main() {
const PriceConsumer = await hre.ethers.getContractFactory("PriceConsumerV3");
const priceConsumer = await PriceConsumer.deploy();
await priceConsumer.deployed();
console.log("Contract deployed to:", priceConsumer.address);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});Deploy the contract:
npx hardhat compile
npx hardhat run --network rinkeby scripts/deploy.jsSave the deployed contract address—you'll need it for the frontend.
👉 Learn how to securely manage keys and deploy contracts with confidence.
Step 3: Build the Frontend with React
Now let’s create a user-friendly interface using React.
Navigate back and create a frontend folder:
cd ..
npx create-react-app frontend
cd frontend
npm install bootstrap ethersClean up unnecessary files (logo.svg, App.test.js, etc.) and open src/App.js.
Replace its content with:
import React, { useEffect, useState } from 'react';
import { ethers } from 'ethers';
import 'bootstrap/dist/css/bootstrap.min.css';
function App() {
const [storedPrice, setStoredPrice] = useState('');
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contractAddress = 'REPLACE_WITH_DEPLOYED_CONTRACT_ADDRESS';
const ABI = '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"getLatestPrice","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"storeLatestPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storedPrice","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"}]';
const contract = new ethers.Contract(contractAddress, JSON.parse(ABI), signer);
const getStoredPrice = async () => {
try {
const price = await contract.storedPrice();
setStoredPrice(Number(price) / 1e8);
} catch (error) {
console.error("Error fetching price:", error);
}
};
const updateNewPrice = async () => {
try {
const tx = await contract.storeLatestPrice();
await tx.wait();
await getStoredPrice();
} catch (error) {
console.error("Error updating price:", error);
}
};
useEffect(() => {
getStoredPrice();
}, []);
return (
<div className="container mt-5">
<h1>ETH/USD Price dApp</h1>
<div className="row mt-4">
<div className="col-md-6">
<h4>Current Stored Price: ${storedPrice}</h4>
</div>
<div className="col-md-6">
<button className="btn btn-primary" onClick={updateNewPrice}>
Update Price from Chainlink
</button>
</div>
</div>
</div>
);
}
export default App;Start the app:
npm startConnect your MetaMask wallet when prompted, and click “Update Price” to interact with your smart contract.
Frequently Asked Questions (FAQ)
Q: What is a dApp?
A: A decentralized application (dApp) runs on a blockchain using smart contracts instead of centralized servers, offering transparency, immutability, and trustless operation.
Q: Do I need cryptocurrency to develop a dApp?
A: Yes, you’ll need testnet ETH to deploy contracts and pay gas fees during development. You can get free Rinkeby ETH from faucets like Chainlink’s official faucet.
Q: Can I modify a smart contract after deployment?
A: No—smart contracts are immutable. Once deployed, they cannot be changed. Developers often use upgradeable proxy patterns for flexibility.
Q: Why use Chainlink in a dApp?
A: Blockchains can't access external data natively. Chainlink provides secure, decentralized oracle services to bring real-world data—like asset prices—into smart contracts.
Q: Is React required for dApp frontends?
A: No. Any frontend framework works—Vue, Angular, or even plain JavaScript. React is popular due to its component-based architecture and strong Web3 library support.
Q: How do users interact with my dApp?
A: Users connect via Web3 wallets like MetaMask. These wallets sign transactions securely, allowing users to approve interactions with your smart contract.
👉 Explore advanced dApp development techniques and accelerate your Web3 journey today.
Final Thoughts
Building a dApp involves combining blockchain logic with modern web development practices. With tools like Hardhat, React, and Chainlink, developers can create powerful, secure, and user-friendly decentralized applications that push the boundaries of what’s possible in Web3.
Whether you're tracking asset prices, creating NFT marketplaces, or building DAOs, mastering these fundamentals is your first step toward innovation in the decentralized world.
Core keywords naturally integrated: dApp development, smart contract, Chainlink, Ethereum, Web3, React, Hardhat, decentralized application.