Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome to Module 9. In the previous modules, we've done all the heavy lifting on the blockchain side: we designed, built, and rigorously tested our ERC-20 token and staking contracts. Now, it's time to build the other half of the application: the user interface that people will use to interact with your smart contracts.
This lesson marks our transition from backend Solidity development to frontend dApp development. Our goal today is to set up a React application with TypeScript for your token's user interface.
Given your background in backend development with TypeScript, you'll find many concepts familiar. We'll focus on establishing a modern project structure and understanding how TypeScript's type-safety is applied within the React framework. This will create the solid foundation we need to build out our dApp's features in the upcoming lessons.
1. Choosing Your Frontend Tooling: Vite
In the past, create-react-app (CRA) was the standard for bootstrapping React projects. However, the ecosystem has evolved, and today, Vite is the recommended tool. It offers a significantly faster development experience, near-instant server start-up, and lightning-fast Hot Module Replacement (HMR), which updates your app in the browser as you code without a full page reload.
We will use Vite to create our React project with TypeScript support.
2. Initializing Your React + TypeScript Project
Let's get our project set up. The following resource from the MetaMask documentation provides a clean, modern guide to initializing a project with Vite, which we will follow.
Create a React dapp with local state
We'll use the official MetaMask documentation, 'Create a React dapp with local state', as our guide for the initial setup. It uses the exact stack we need: Vite, React, and TypeScript.
Follow the instructions in the section '1. Set up the project'. This will guide you to run npm create vite@latest ..., install dependencies, and start the development server. You can stop after you've run npm run dev and see the default React page in your browser.
After following those steps, your project directory should look something like this:
vite-react-dapp/
āāā node_modules/
āāā public/
ā āāā vite.svg
āāā src/
ā āāā assets/
ā ā āāā react.svg
ā āāā App.css
ā āāā App.tsx
ā āāā index.css
ā āāā main.tsx
ā āāā vite-env.d.ts
āāā .eslintrc.cjs
āāā .gitignore
āāā index.html
āāā package.json
āāā tsconfig.json
āāā vite.config.ts
When you run npm run dev, you should see the default Vite + React application running in your browser.

3. Cleaning the Slate
Before we start building, let's remove the boilerplate code from the default project.
- In
src/App.tsx, replace the entire content with a basic component structure:import './App.css' function App() { return ( <div className="App"> <h1>My Token dApp</h1> </div> ) } export default App - In
src/App.css, delete all the existing CSS rules. We'll add our own styles later.
Your browser should now display a simple page with the heading "My Token dApp". We now have a clean slate to begin our work.
4. TypeScript in a React Context: A Refresher
Your experience with TypeScript is a significant advantage. However, applying it within React involves specific patterns for typing components, state, and props. Let's do a quick, practical review of these core concepts.
The following video from freeCodeCamp.org is an excellent resource for this. We'll watch specific segments that cover the most important patterns you'll use daily when building the dApp.
React & TypeScript - Course for Beginners
This video, 'React & TypeScript - Course for Beginners', provides clear, concise examples of how to use TypeScript within a React application. We'll focus on the essential parts for our dApp.
This is the most important part of the lesson. Please watch the following segments carefully: Fundamental Types & Interfaces (4:50 - 17:18): Focus on how interface and type are used to define the shape of objects. This is how we'll define our data models (e.g., token info, user balances). Typing Functional Components (18:15 - 19:19): Pay attention to the use of React.FC to type a component. Typing State with useState (26:18 - 29:28): Understand how to provide a type to the useState hook, for both simple values and complex objects. Creating Data Models (30:26 - 32:29): This shows how to create a separate model.ts file for an interface (Todo). This is a best practice we will use for our contract data. Typing Events (33:37 - 35:20): Learn how to type event handlers, like for a form submission, using types like React.FormEvent.
Let's summarize the key patterns from the video that are most relevant to our dApp:
-
Interfaces for Data Models: Just as the video created a
Todointerface inmodel.ts, we will create interfaces for our data. For example, we might create aTokenInfointerface:// in src/models/token.ts export interface TokenInfo { name: string; symbol: string; totalSupply: string; // Using string since balances can be very large numbers } -
Typing Component Props: When we create components, we'll use interfaces to define their props.
interface BalanceDisplayProps { tokenSymbol: string; balance: string; } const BalanceDisplay: React.FC<BalanceDisplayProps> = ({ tokenSymbol, balance }) => { return ( <div> <p>Your Balance: {balance} {tokenSymbol}</p> </div> ); }; -
Typing State: We'll use generics with the
useStatehook to ensure our state is type-safe.import { useState } from 'react'; import { TokenInfo } from './models/token'; const [tokenData, setTokenData] = useState<TokenInfo | null>(null); const [isLoading, setIsLoading] = useState<boolean>(true);
Test your understanding!
You need to create a React component that takes a user's wallet address (a string) as a prop and also manages a state variable for that user's token balance (which will also be a string).
How would you define the props interface and the useState hook for this component?
Show answer
import { useState } from 'react';
// 1. Define the props interface
interface UserProfileProps {
address: string;
}
const UserProfile: React.FC<UserProfileProps> = ({ address }) => {
// 2. Define the state for the balance
const [balance, setBalance] = useState<string>("0");
// ... component logic to fetch and display the balance ...
return (
<div>
<p>Address: {address}</p>
<p>Balance: {balance}</p>
</div>
);
};
5. Installing Ethers.js
Our React app is set up, but it doesn't know how to talk to the Ethereum blockchain yet. For that, we need the ethers.js library. You're already familiar with it from our Hardhat tests; now we'll install it in our frontend project.
Open your terminal in the project root and run the following command:
npm install ethers
This makes the ethers library available for us to import and use in our components to connect to MetaMask, read data from our smart contracts, and send transactions.
Conclusion
Excellent work! We've successfully laid the groundwork for our dApp's frontend. You now have a running React application built with modern tooling and a clear understanding of how to apply your TypeScript knowledge within it.
Key Takeaways:
- Vite is the modern, fast way to build React applications.
- The setup process involves initializing with
npm create vite, installing dependencies, and running the dev server. - TypeScript in React relies on specific patterns:
- Using
interfaceortypeto define the shape of props and state. - Typing functional components with
React.FC<PropsInterface>. - Providing a type to the
useStatehook using generics:useState<MyType>(initialValue).
- Using
ethers.jsis the essential library that will bridge our React app with the Ethereum blockchain.
Preview of the Next Lesson:
In our next lesson, we will bring the dApp to life. We will integrate ethers.js to connect the frontend to the Ethereum network. You will build the "Connect Wallet" button and write the logic to prompt the user with MetaMask, retrieve their account address, and display it in the application. This is the first crucial step in making your dApp interactive.