Three Steps to Develop a dApp

·

Developing a decentralized application (dApp) is an exciting journey into the world of Web3, where blockchain technology enables trustless, transparent, and tamper-proof applications. In this comprehensive guide, you'll learn how to build an end-to-end dApp that retrieves and stores the current price of ETH using a smart contract and a React-based frontend. By the end, you’ll have a working prototype that interacts with real blockchain data — no prior blockchain development experience required.

Whether you're a beginner or an experienced developer exploring Web3, this tutorial breaks down the process into three clear steps: creating a smart contract, deploying it on a test network, and building a user-friendly frontend.

👉 Discover how to turn blockchain concepts into real-world dApps with step-by-step guidance.

What Is a dApp?

A decentralized application (dApp) differs from traditional apps by replacing centralized backend servers with blockchain-based logic. Instead of relying on a single server, dApps run their backend code on a distributed network through smart contracts — self-executing programs stored on the blockchain.

While the frontend (UI) of a dApp can be built using standard web technologies like HTML, CSS, and JavaScript, its core logic lives on-chain, making it resistant to censorship, downtime, and manipulation.

Key Advantages of dApps

However, these benefits come with trade-offs:

Despite these challenges, dApps are powering innovation across DeFi, NFTs, gaming, and more.

Core Components of a dApp

Every dApp consists of three main components:

Smart Contracts

Smart contracts define the business logic and maintain the state of the application on the blockchain. This is what makes dApps secure and autonomous. They handle tasks like data validation, transaction execution, and state changes.

Frontend / User Interface (UI)

The frontend is typically built with familiar tools like React, Vue, or Angular. It connects to the smart contract using libraries such as ethers.js or web3.js, allowing users to interact with the blockchain through their Web3 wallet (e.g., MetaMask).

Data Storage

Storing large datasets directly on-chain is expensive and inefficient. Most dApps use off-chain solutions like IPFS (InterPlanetary File System) or Filecoin for bulk data storage, while keeping critical state and logic on-chain.

This hybrid approach balances cost, speed, and decentralization.


Step 1: Create a Smart Contract

Our dApp will fetch the current ETH/USD price from Chainlink’s decentralized oracle network and store it permanently in a smart contract.

We’ll use Solidity, the most popular language for Ethereum smart contracts, along with Hardhat, a powerful development environment.

Set Up Your Development Environment

First, create a project folder and initialize Hardhat:

mkdir chainlink-dapp-example
cd chainlink-dapp-example
mkdir backend
cd backend
npm init -y
npm install --save-dev hardhat
npx hardhat

Choose "Create a JavaScript project" and accept default settings.

Install required dependencies:

npm install --save-dev @nomicfoundation/hardhat-toolbox
npm install @chainlink/contracts --save
npm install dotenv

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:

👉 Learn how to integrate real-world data into your smart contracts seamlessly.

Step 2: Deploy the Smart Contract

To deploy, configure Hardhat to connect to the Rinkeby testnet.

Configure hardhat.config.js

Replace its content with:

require("@nomicfoundation/hardhat-toolbox");
require('dotenv').config();

const RINKEBY_RPC_URL = process.env.RINKEBY_RPC_URL || "";
const PRIVATE_KEY = process.env.PRIVATE_KEY || "";

module.exports = {
    defaultNetwork: "rinkeby",
    networks: {
        rinkeby: {
            url: RINKEBY_RPC_URL,
            accounts: [PRIVATE_KEY],
        },
    },
    solidity: "0.8.9",
};

Set Up Environment Variables

Create a .env file in the backend folder:

RINKEBY_RPC_URL=your_rpc_url_here
PRIVATE_KEY=your_wallet_private_key_here
🔐 Use a dedicated test wallet with no real funds. Never expose your mainnet private key.

Obtain an RPC URL from Infura or Alchemy, then fund your wallet using the Chainlink Faucet.

Deploy Script

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 with:

npx hardhat compile
npx hardhat run --network rinkeby scripts/deploy.js

Save the deployed contract address — you’ll need it for the frontend.

Step 3: Build the Frontend

We’ll use React and ethers.js to create a simple UI that connects to your deployed contract.

Initialize React App

cd ..
npx create-react-app frontend
cd frontend
npm install bootstrap ethers

Remove 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, ABI, signer);

    const getStoredPrice = async () => {
        try {
            const contractPrice = await contract.storedPrice();
            setStoredPrice(Number(contractPrice) / 1e8); // Adjust for 8 decimals
        } catch (error) {
            console.log("Error fetching price:", error);
        }
    };

    const updateNewPrice = async () => {
        try {
            const tx = await contract.storeLatestPrice();
            await tx.wait();
            await getStoredPrice();
        } catch (error) {
            console.log("Error updating price:", error);
        }
    };

    useEffect(() => {
        if (window.ethereum) {
            getStoredPrice();
        }
    }, []);

    return (
        <div className="container mt-5">
            <h1>ETH/USD Price dApp</h1>
            <div className="row">
                <div className="col-md-6">
                    <div className="card">
                        <div className="card-body">
                            <h5 className="card-title">Stored Price</h5>
                            <p className="card-text">ETH/USD: {storedPrice}</p>
                        </div>
                    </div>
                </div>
                <div className="col-md-6">
                    <div className="card">
                        <div className="card-body">
                            <h5 className="card-title">Update Price</h5>
                            <button className="btn btn-primary" onClick={updateNewPrice}>
                                Update
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default App;

Start the app:

npm run start

Connect your MetaMask wallet when prompted and click “Update” to sync the latest ETH price.

Frequently Asked Questions (FAQ)

What is a dApp?

A decentralized application (dApp) runs its backend logic on a blockchain via smart contracts instead of centralized servers. It offers transparency, censorship resistance, and trustless interactions.

Do I need cryptocurrency to develop a dApp?

You don’t need real funds to develop one, but testnet tokens (like Rinkeby ETH) are required to deploy contracts and simulate transactions during development.

Can I update my smart contract after deployment?

Most smart contracts are immutable. Once deployed, they cannot be changed. Developers use upgradeable patterns cautiously, but immutability is a core principle of blockchain security.

What tools are essential for dApp development?

Key tools include Hardhat or Truffle for development, MetaMask for wallet interaction, ethers.js for frontend connectivity, and IPFS for decentralized storage.

How do users interact with my dApp?

Users connect via Web3 wallets like MetaMask. These allow them to sign transactions securely without sharing private keys with the app.

Is React necessary for dApp frontends?

No — any frontend framework works. React is popular due to its component architecture and strong Web3 library support.

👉 Start building your first dApp with confidence using expert-backed tools and resources.

Conclusion

Building a dApp involves three core stages: writing smart contracts, deploying them on-chain, and connecting them to a user-friendly frontend. This tutorial walked you through creating a functional dApp that fetches real-time cryptocurrency prices using Chainlink oracles and stores them immutably on Ethereum’s testnet.

With foundational knowledge of Solidity, Hardhat, React, and ethers.js, you're now equipped to explore more complex projects — from DeFi protocols to NFT marketplaces.

As Web3 continues to evolve, mastering dApp development opens doors to innovative, decentralized solutions that empower users worldwide.

Core Keywords: dApp development, smart contracts, blockchain applications, Web3 technology, decentralized apps, Ethereum dApp, Chainlink oracle, React dApp