Connect Metamask wallet to a React App

Hi, I’m Chineta Adinnu! I’m a frontend developer with a passion for creating dynamic and user-friendly web experiences. On this blog, I share my journey in frontend development, from learning new technologies to implementing them in real projects.
I dive into frameworks like React and Next.js, explore state management with Redux Toolkit, and offer practical tips based on my hands-on experience. My goal is to provide valuable insights and tutorials that can help others navigate the ever-evolving world of web development.
Join me as I document my learning process, share challenges and solutions, and offer guidance on the latest frontend technologies. If you’re interested in tech or looking for practical advice, you’re in the right place!
Feel free to connect if you have questions or want to discuss tech!
Check out some of my articles on Medium: https://medium.com/@chinetaadinnu."
Introduction
I got referred by someone to create a frontend react app for a web3 project. I have never developed anything in the web3 space but I liked the challenge so I took it up.
My task was to connect MetaMask and Solana Phantom wallets to a React app.
I had to do a lot of research to get this working and I will be dropping links to resources that were helpful at the end of this tutorial.
Side note: This article was supposed to contain MetaMask and Phantom wallet connection to a React app. However, after writing about MetaMask connection I realized the article was getting too long so I decided to break it up and write about Phantom wallet connection in another article.
What we are building
We will be building a simple button that prompts a modal with MetaMask and Phantom wallet options so users can select which wallet to connect to.
The user's wallet address is also going to be displayed on the Navbar after they connect.
Let's get started!
Getting started
To get started, we have to create a react app called web3-wallet
I'm assuming you are already familiar with React setup so I won't go into that. If you aren't you can read about setting up your react app here
We delete everything in src except index.js, app.js, and index.css
Now, let's build out a basic React app that the wallets will connect to.
Creating a basic React app
We will create a navbar with a connect button for users to click on.
I'll be using tailwindCSS for the styling because I love it 😍
Tailwind setup
To install tailwindCSS, run the following command on your terminal
yarn add -D tailwindcss postcss autoprefixer
Create a tailwind.config.js file on your root folder and paste this
module.exports = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}
Also, create postcss.config.js file on your root folder and paste this
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
Lastly, add the following to your index.css file
@tailwind base;
@tailwind components;
@tailwind utilities;
Navbar component
In src, create a folder called components that will host all the components used on this app.
In the components folder, create a component called Navbar.js The Navbar will have a logo, and a button and also display the wallet address when a user connects to a wallet.
import React from 'react';
const Navbar = () => {
return (
<nav className='lg:px-52 px-6 py-8 bg-[#8b5cf6] relative'>
<div className='flex justify-between transition-all duration-500
ease-in rounded'>
<div className='text-white font-bold text-3xl'>LOGO</div>
<h3 className='text-white font-semibold mr-4'>Wallet address:</h3>
<button
type='button'
className='my-6 lg:my-0 bg-white text-primary-200 font-semibold
py-2 px-6 items-center rounded-lg flex justify-center >
Connect
</button>
</div>
</nav>
);
};
export default Navbar;
In App.js, let's clear the boilerplate code and import Navbar
import React from 'react';
import Navbar from './components/Navbar';
const App = () => {
return (
<>
<Navbar />
</>
);
};
export default App;
On the terminal run yarn start to open the app on http://localhost:3000/
We can see our Navbar with the connect button on it.

We want the button to open up a modal and we'll use useState hook to keep track of the state of the modal.
Since this state is going to be accessed by more than one component, it is best practice to keep it in a common parent component, which in this case is App.js
In App.js, import useState and create a state called modalOpen setting the initial value to false.
To open the Modal, modalOpen needs to be true, so let's pass this as a props in the Navbar component.
import React, { useState } from 'react';
import Navbar from './components/Navbar';
const App = () => {
const [modalOpen, setModalOpen] = useState(false);
return (
<>
<Navbar openModal={() => setModalOpen(true)} />
</>
);
};
export default App;
In the Navbar.js file, import openModal and pass it to the onClick event of the connect button.
const Navbar = ({ openModal }) => {
return (
<nav className='lg:px-52 px-6 py-8 bg-[#8b5cf6] relative'>
<div className='flex justify-between transition-all duration-500
ease-in rounded'>
<div className='text-white font-bold text-3xl'>LOGO</div>
<h3 className='text-white font-semibold mr-4'>Wallet address:</h3>
<button
type='button'
className='my-6 lg:my-0 bg-white text-primary-200 font-semibold py-2
px-6 items-center rounded-lg flex justify-center '
onClick={openModal}
>
Connect
</button>
</div>
</nav>
);
};
export default Navbar;
To check if this works, add console.log(modalOpen) to the App.js file.
When we inspect the app, we can see false logged on the console and when the connect button is clicked true gets logged on the console. So it works!
Now, we can build the Modal component.
Modal component
In the components folder, create a file called Modal.js.
We also need to pass the modalOpen state to the Modal component. Let's import this component to App.js so that we can pass the state as props.
return (
<>
<Navbar openModal={() => setModalOpen(true)} />
<Modal isOpen = {modalOpen}/>
</>
);
In Modal.js, import the isOpen props
Remember modalOpen needs to be true to render the Modal component, so let's write a condition to this effect.
import React from 'react';
const Modal = ({ isOpen }) => {
return <>{isOpen ? <h3>I'm open</h3> : null}</>;
};
export default Modal;
This condition renders the Modal only when the state is true, and the state becomes true when the connect button is clicked.
We can see I'm open text displayed on the screen when the connect button is clicked
To close the Modal, we have to set modalOpen to false.
Back in App.js, set modalOpen to false and pass it as props to the Modal component.
<Modal isOpen={modalOpen} onClose={()=>setModalOpen(false)} />
Let's import onClose in the Modal component and style the component.
import React from 'react';
const Modal = ({ isOpen, onClose }) => {
return (
<>
{isOpen ? (
<div className='bg-[rgba(0,0,0,0.5)] top-0 left-0 w-full h-screen absolute'>
<div className=' right-0 left-0 z-50 lg:w-4/12 absolute bg-white p-4 top-20
h-full mx-auto'>
<div className=' py-4 flex px-6 justify-end'>
<button
className='border border-current rounded px-4 py-1 text-xl font-semibold
cursor-pointer'
onClick={onClose}
>
Close
</button>
</div>
<h3 className='text-center text-2xl font-semibold'>
Choose Wallet to continue
</h3>
</div>
</div>
) : null}
</>
);
};
export default Modal;
Now, we have a toggle between the connect button on the Navbar and the close button on the Modal.
Wallets component
Let's create a Wallets component to hold wallet details for MetaMask and Phantom.
In the Wallets.js file, create an array of objects. This array can also be saved in another file and imported here if you like.
const walletList = [
{
name: 'Phantom',
icon: <img src={PhantomLogo} alt='phantom logo' className='w-[30px]' />,
button: (
<button className='bg-[#512da8] hover:bg-[#1a1f2e] text-white
py-3 px-5 rounded-[4px]'>
Connect
</button>
),
},
{
name: 'MetaMask',
icon: <Icon icon='logos:metamask-icon' className='text-2xl' />,
button: (
<button className='bg-[#512da8] hover:bg-[#1a1f2e] text-white
py-3 px-5 rounded-[4px]'>
Connect
</button>
),
},
];
For the icons, I downloaded an image for Phantom wallet and saved it in the assets folder inside src. For MetaMask, I used a MetaMask icon from iconify. You can install iconify by running yarn add -D @iconify/react
We will map through walletList array to render wallet details.
{walletList.map((wallet) => (
<div
key={wallet.name}
className='flex items-center justify-between mb-5 px-4'
>
<div className='flex'>
<span className='mr-3'>{wallet.icon}</span>
<p> {wallet.name}</p>
</div>
<div> {wallet.button}</div>
</div>
))}
Now, we can import Wallets component inside the Modal component
import React from 'react';
import Wallets from './Wallets';
const Modal = ({ isOpen, onClose }) => {
return (
<>
{isOpen ? (
<div className='bg-[rgba(0,0,0,0.5)] top-0 left-0 w-full h-screen absolute'>
<div className=' right-0 left-0 z-50 lg:w-4/12 absolute bg-white p-4
top-20 h-full mx-auto'>
<div className=' py-4 flex px-6 justify-end'>
<button
className='border border-current rounded px-4 py-1 text-xl
font-semibold cursor-pointer'
onClick={onClose}
>
Close
</button>
</div>
<h3 className='text-center text-2xl font-semibold'>
Choose Wallet to continue
</h3>
<Wallets />
</div>
</div>
) : null}
</>
);
};
export default Modal;
We have built a basic React app and can now connect MetaMask and Phantom wallets to it.
Connecting MetaMask
I found MetaMask pretty straightforward and easy to connect, so I'll start with it.
First off, we add MetaMask extension to our browser.
If you are using chrome browser, click here to add MetaMask to browser, then create a MetaMask account if you don't already have one.
Now we install some dependencies to aid with the connectivity. I'll attach resources to explain what these dependencies do.
yarn add web3 @web3-react/core @web3-react/injected-connector
In App.js we import Web3 and Web3ReactProvider from the dependencies we installed.
We create getLibrary function and pass the function as props to the Web3ReactProvider, then we wrap our components in App.js with the Web3ReactProvider.
import React, { useState } from 'react';
import Modal from './components/Modal';
import Navbar from './components/Navbar';
import Web3 from 'web3';
import { Web3ReactProvider } from '@web3-react/core';
const App = () => {
const [modalOpen, setModalOpen] = useState(false);
const getLibrary = (provider) => {
return new Web3(provider);
};
return (
<>
<Web3ReactProvider getLibrary={getLibrary}>
<Navbar openModal={() => setModalOpen(true)} />
<Modal isOpen={modalOpen} onClose={() => setModalOpen(false)} />
</Web3ReactProvider>
</>
);
};
export default App;
Create a folder called helper in src and a file called helper.js in the folder.
In helper.js, import InjectedConnector from @web3-react/injected-connector
import { InjectedConnector } from '@web3-react/injected-connector';
export const injected = new InjectedConnector({
supportedChainIds: [1, 3, 4, 5, 42],
});
When a user is connected to MetaMask, we want to be able to get the active status, account information, and some other details and we want to use these details in several components on the app. To avoid props drilling, let's use Context API to pass these props around.
Create a folder in src called context, Let's call the context MetaMaskWalletContext.js
Here we can import useWeb3React and gain access to MetaMask wallet details
import React, { createContext } from 'react';
import { useWeb3React } from '@web3-react/core';
export const MetaMaskWalletContext = createContext();
export const MetaMaskWalletProvider = ({ children }) => {
const { active, activate, deactivate, account } = useWeb3React();
return (
<MetaMaskWalletContext.Provider
value={{ active, activate, deactivate, account }}
>
{children}
</MetaMaskWalletContext.Provider>
);
};
export default MetaMaskWalletProvider;
If we need to extract more details from useWeb3React we can just add them later on.
In App.js, let's import MetaMaskWalletProvider and wrap our components with it.
import React, { useState } from 'react';
import Modal from './components/Modal';
import Navbar from './components/Navbar';
import Web3 from 'web3';
import { Web3ReactProvider } from '@web3-react/core';
import { MetaMaskWalletProvider } from './context/MetaMaskWalletContext';
const App = () => {
const [modalOpen, setModalOpen] = useState(false);
const getWeb3Library = (provider) => {
return new Web3(provider);
};
return (
<>
<Web3ReactProvider getWeb3Library={getWeb3Library}>
<MetaMaskWalletProvider>
<Navbar openModal={() => setModalOpen(true)} />
<Modal isOpen={modalOpen} onClose={() => setModalOpen(false)} />
</MetaMaskWalletProvider>
</Web3ReactProvider>
</>
);
};
export default App;
Back in Wallets.js, we can use MetaMaskWalletContext and access the details we require.
Let's write a function for the connect button onClick event.
We import injected from the helper file and pass it into activate
import { MetaMaskWalletContext } from '../context/MetaMaskWalletContext';
import { injected } from '../helper/helper';
const { active, activate } = useContext(MetaMaskWalletContext);
async function connect() {
try {
await activate(injected);
} catch (ex) {
console.log(ex);
}
}
button: (
<button
className='bg-[#512da8] hover:bg-[#1a1f2e] text-white
py-3 px-5 rounded-[4px]'
onClick={connect}
>
Connect
</button>
Now when we click on connect, MetaMask opens up on the browser and asks for password to login. Cool!
Let's make the modal close when a user is connected to MetaMask.
We already wrote an onClose function for the modal close button so we can just pass it down as props to the Wallets component.
<Wallets onClose={onClose} />
In Wallets.js, let's write a logic to close the modal when the connection is active. We check if a connection is active and trigger onClose
if (active) {
onClose();
}
The next thing we need to do is to display the Wallet address on the navbar.
In Navbar.js, import the MetaMaskWalletContext so we can access account and active status
import { MetaMaskWalletContext } from '../context/MetaMaskWalletContext';
const { account, active } = useContext(MetaMaskWalletContext);
<h3 className='text-white font-semibold mr-4'>
{active ? `Wallet address: ${account}` : ''}
</h3>
Now when we connect, we can see the wallet address clearly on the dashboard.
Let's also change the connect button to disconnect and also write a function to disconnect a user from MetaMask.
In Navbar.js
import { injected } from '../helper/helper';
async function disconnect() {
try {
await deactivate(injected);
} catch (ex) {
console.log(ex);
}
}
const handleConnection = () => {
if (active) {
disconnect();
} else {
openModal();
}
};
<button
type='button'
className='my-6 lg:my-0 bg-white text-primary-200 font-semibold py-2
px-6 items-center rounded-lg flex justify-center '
onClick={handleConnection}
>
{active ? 'Disconnect' : 'Connect'}
</button>
Tada! there we have it😆
We can connect to MetaMask wallet, display the wallet address, and disconnect when we want.
I hope this article has been helpful. Tell me what you think by dropping a comment.😃
Github repo : https://github.com/Netacci/metamask-connect
Metamask resources : https://www.youtube.com/watch?v=DCA53Go5ON8&t=622s&ab_channel=ShmojiCodes




