Web3-React (V8)
This tutorial is a step-by-step guide on how to integrate a wallet such as Phantom into your dApp using the Web3-React (v8) library. Web3-React is an EVM library that exposes hooks for common web3 primitives such as wallets, addresses, and the like.
We will be going through step by step how to go from zero to a fully integrated web3-react-powered Phantom connect button.
- Node version >=16.12.0
- A text editor/IDE (such as VSCode)
- Some Knowledge of React
- Yarn (v1)
To create a new React application using Vite, run the following command in your terminal:
yarn create vite
- 1.This will ask you for a project name. Provide it a name here. For purposes of this tutorial I used "Web3-React-V8-Sandbox".
- 2.It will then ask you to select a framework. Select "React" here.
- 3.Next it will ask for a variant. Select "Typescript" here.
Now change directory into your project and run:
yarn install
And make sure your app runs by running the command:
yarn dev
You will need to change the
moduleResolution
keys to be switched from "bundler" to "node".Each file should look as follows.
tsconfig.json
tsconfig.node.json
{
"compilerOptions": {
"target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
With that out of the way we can install all of our dependencies.
To install the appropriate packages, run the following:
yarn add web3-react-phantom @web3-react/core eventemitter3 @web3-react/types
Now you can use the web3-react and web3-react-phantom packages in your project and integrate Phantom as a wallet in the app.
Web3-react is entirely based around the idea of connectors, and how it handles them. In a nutshell, you configure all of the wallets you'll need up front, and then web3-react works as a huge global state machine, that allows you to interact with any wallet anywhere in your application.
To begin, create a file in the root of your directory called
connectors.ts
it should look like the following// connectors.ts
import { initializeConnector, Web3ReactHooks } from '@web3-react/core'
import { Connector, Web3ReactStore } from '@web3-react/types'
import { Phantom } from 'web3-react-phantom'
const phantom = initializeConnector<Phantom>((actions) => new Phantom({ actions }))
const connectors: [Connector, Web3ReactHooks, Web3ReactStore][] = [phantom]
export default connectors
Here you Initialize all of the connectors that you want in your app, and then you can pass them all to the
connectors
array, and export it for consumption in your app.First we will need to import the packages into our project. At the top of the
main.tsx
file add these imports:// main.tsx
import { Web3ReactProvider, Web3ReactHooks } from '@web3-react/core'
import { Connector } from '@web3-react/types'
import allConnections from './connectors'
Next, we will configure web3-react like so.
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import { Web3ReactProvider, Web3ReactHooks } from '@web3-react/core'
import { Connector } from '@web3-react/types'
import allConnections from './connectors'
const connections: [Connector, Web3ReactHooks][] = allConnections.map(([connector, hooks]) => [connector, hooks])
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<Web3ReactProvider connectors={connections}>
<React.StrictMode>
<App />
</React.StrictMode>
</Web3ReactProvider>
)
This will wrap our Vite app in this
<Web3ReactProvider>
JSX component which will give us global access to the hooks that we need to interact with Phantom.We also map through all of the arrays as the connector has 3 elements by default, and the
<Web3ReactProvider>
element only expects 2, so we want to pull the 3rd one out before passing it to the wrapper.We have everything we need done to add a connect wallet button to the app. But before we do, let's create a
Card
componentIn the root of your app you can create a folder called
components
and inside of that folder, create a file and name it Card.tsx
//Card.tsx
import { useEffect, useState } from 'react'
import { Web3ReactSelectedHooks } from '@web3-react/core'
import { Connector } from '@web3-react/types'
export default function Card({connector, hooks, name}: {connector: Connector, hooks: Web3ReactSelectedHooks, name: string}) {
const {useSelectedAccount, useSelectedChainId, useSelectedIsActive, useSelectedIsActivating } = hooks
const isActivating = useSelectedIsActivating(connector)
const isActive = useSelectedIsActive(connector)
const account = useSelectedAccount(connector)
const chain = useSelectedChainId(connector)
const [error, setError] = useState<Error | undefined>(undefined)
const [connectionStatus, setConnectionStatus] = useState('Disconnected')
const handleToggleConnect = () => {
setError(undefined) // clear error state
if (isActive) {
if(connector?.deactivate) {
void connector.deactivate()
} else {
void connector.resetState()
}
}
else if (!isActivating) {
setConnectionStatus('Connecting..')
Promise.resolve(connector.activate(1))
.catch((e) => {
connector.resetState()
setError(e)
})
}
}
useEffect(() => {
if(isActive) {
setConnectionStatus('Connected')
} else {
setConnectionStatus('Disconnected')
}
}
,[isActive])
return (
<div>
<p>{name.toUpperCase()}</p>
<h3>Status - {(error?.message) ? ("Error: " + error.message) : connectionStatus}</h3>
<h3>Address - {account ? account : "No Account Detected"}</h3>
<h3>ChainId - {chain ? chain : 'No Chain Connected'}</h3>
<button onClick={handleToggleConnect} disabled={false}>
{isActive ? "Disconnect" : "Connect"}
</button>
</div>
)
}
While this may look a bit complicated, we can break it down a bit.
What we will be passing the component are
connector
, hooks
, and name
props.We can run through what each of them is going to do.
Prop | Purpose |
---|---|
connector | You can think of this as the wallet itself. It will be directly responsible for connecting, disconnecting, and sending requests to the blockchain. |
hooks | These are an assortment of convenience hooks that will grab information about the wallet/connection that you are handling. |
name | This is a string that we'll use to label the name of the connection to display in the UI. |
The rest of the component is calling the hooks to get the relevant status connection, address, and chain id. Then we add some state management to display whether or not the wallet is connected, disconnected, or in the process of connecting.
Then we display all of the relevant information, and and show connect/disconnect buttons.
At the top of your
App.tsx
file you can import the Card
component we just made and the useWeb3React
hook that we will use to access the values we stored in the wrapper up in main.tsx
.import { useWeb3React } from "@web3-react/core";
import Card from "./components/Card";
Next, you will call the
useWeb3React
hook at the top of your App
componentfunction App() {
const { connector, hooks } = useWeb3React(); // <-- This line
//return ( ... )
}
Then delete the useState hook, it's import, and the button that increments it as we do not need the counter anymore.
Then add the
Card
component and pass in the connector and hooks into it. Where the counter button used to be.<Card connector={connector} hooks={hooks} name='phantom' />
Your entire
App.tsx
file should look like soimport reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
import { useWeb3React } from "@web3-react/core";
import Card from "./components/Card";
function App() {
const { connector, hooks } = useWeb3React();
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="App">
<h1>Web3-React Connector Playbox</h1>
<div className="card">
<Card connector={connector} hooks={hooks} name='phantom' />
</div>
</div>
</>
);
}
export default App;
Now when you run everything locally it should look like this!

And then once you click the connect button, it will pop open phantom's connect screen. Once you hit approve all of your information should be displayed like so

You've now added your very own Phantom connect button using Web3-React!
You can use this as the basis for your own dApp if you're looking to build out something, or use it for reference to add a Phantom connect button to your existing dApp.
Either way, we hope you found this useful.