Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Setting Up a React-TypeScript DApp UI

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.

Vite + React Initial Setup Display
This is the default screen for a new Vite + React project, confirming that your development environment is correctly set up and running.

3. Cleaning the Slate

Before we start building, let's remove the boilerplate code from the default project.

  1. 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
    
  2. 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 Todo interface in model.ts, we will create interfaces for our data. For example, we might create a TokenInfo interface:

    // 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 useState hook 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 interface or type to define the shape of props and state.
    • Typing functional components with React.FC<PropsInterface>.
    • Providing a type to the useState hook using generics: useState<MyType>(initialValue).
  • ethers.js is 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.

Can't find a good explanation? Sign up and we'll make it for you

Sign up