# Connecting to web3 with Web3Modal, react & ethers.js 

By [woj.eth](https://paragraph.com/@woj) · 2021-10-15

---

I’m writing this article solely with hope that one day it ends up high in web searches for this specific problem & it saves lives of devs like me.

Or at least I will be sending that to friends starting out with building web3 apps 😄

Why should I use Web3Modal?
---------------------------

Web3Modal allows you to connect your app to many wallet providers in a unified way. It’s a real blessing if you’re a dev trying to develop a web3 mvp really fast, but don’t want to compromise on wallet diversity.

Here is their Github repo

[https://github.com/Web3Modal/web3modal](https://github.com/Web3Modal/web3modal)

How to use Web3Modal to connect to wallets?
-------------------------------------------

It’s very simple! First, let’s start with an empty component

    export default function Home(props) {
    
      return <button onClick={}>Connect</button>
    }
    

and paste the setup code from [the readme](https://github.com/Web3Modal/web3modal#using-in-vanilla-javascript).

    import Web3Modal from 'web3modal'
    import WalletConnectProvider from '@walletconnect/web3-provider'
    
    import { useState, useEffect } from 'react';
    
    export default function Home(props) {
    
      const [web3Modal, setWeb3Modal] = useState(null)
    
      useEffect(() => {
        const providerOptions = {
          walletconnect: {
            package: WalletConnectProvider,
            options: {
              infuraId: YOUR_INFURA_KEY,
            }
          },
        };
    
        const newWeb3Modal = new Web3Modal({
          cacheProvider: true, // very important
          network: "mainnet",
          providerOptions,
        });
    
        setWeb3Modal(newWeb3Modal)
      }, [])
    
      return <button onClick={}>Connect</button>
    }
    

Ok, config done. Now it’s time for the actual connection logic!

    import Web3Modal from 'web3modal'
    import WalletConnectProvider from '@walletconnect/web3-provider'
    
    import { useState, useEffect } from 'react';
    
    export default function Home(props) {
    
      const [web3Modal, setWeb3Modal] = useState(null)
    
      useEffect(() => {
        // initiate web3modal
        const providerOptions = {
          walletconnect: {
            package: WalletConnectProvider,
            options: {
              infuraId: YOUR_INFURA_KEY,
            }
          },
        };
    
        const newWeb3Modal = new Web3Modal({
          cacheProvider: true, // very important
          network: "mainnet",
          providerOptions,
        });
    
        setWeb3Modal(newWeb3Modal)
      }, [])
    
      async function connectWallet() {
        const provider = await web3Modal.connect();
      }
    
      return <button onClick={connectWallet}>Connect wallet</button>
    }
    

Perfect! Now we have a “Connect wallet” button, which will display the web3 modal after clicking. Once the user connects their wallet, we can read their data & prompt them to perform transactions or signatures.

Integration with ethers.js (vs web3.js)
---------------------------------------

I’m not picking wars here and web3.js is a great library, but the concept of providers & signers works much better in my head so for any custom on chain interaction I’m using ethers.js

Web3Modal is not _directly_ compatible with ethers, but you can wrap the Web3Modal provider into an ethers provider just like that:

    import { ethers, providers } from "ethers";
    import Web3Modal from 'web3modal'
    import WalletConnectProvider from '@walletconnect/web3-provider'
    
    import { useState, useEffect } from 'react';
    
    export default function Home(props) {
    
      const [web3Modal, setWeb3Modal] = useState(null)
      const [address, setAddress] = useState("")
    
      useEffect(() => {
        // initiate web3modal
        ...
      }, [])
    
      async function connectWallet() {
        const provider = await web3Modal.connect();
        const ethersProvider = new providers.Web3Provider(provider)
        const userAddress = await ethersProvider.getSigner().getAddress()
        setAddress(userAddress)
      }
    
      return (
        <div>
          <button onClick={connectWallet}>Connect wallet</button>
          <p>{address}</p>
        </div>
      )
    }
    

Connecting from cache & disconnecting
-------------------------------------

When I coded my first web3 experiments I googled this phrase a 1000 times:

> ethers how to check if wallet already connected

It wasn’t really helpful as there is no standard way of doing this. Fortunately, if you enable the option `cacheProvider: true` in Web3Modal config, you will be able to automatically connect, without displaying the modal.

Example below!

    // imports
    
    export default function Home(props) {
    
      const [web3Modal, setWeb3Modal] = useState(null)
      const [address, setAddress] = useState("")
    
      useEffect(() => {
        // initiate web3modal
        ...
      }, [])
    
      useEffect(() => {
        // connect automatically and without a popup if user is already connected
        if(web3Modal && web3Modal.cachedProvider){
          connectWallet()
        }
      }, [web3Modal])
    
      async function connectWallet() {
        ...
      }
    
      return (
        <div>
          <button onClick={connectWallet}>Connect wallet</button>
          <p>{address}</p>
        </div>
      )
    }
    

Listeners
---------

With Web3Modal we have an easy way to subscribe to wallet events like network or account changes. I recommend doing that over playing with ethers provider, since the latter option was very messy for me.

Web3Modal listeners obey [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) standard and you can easily integrate them like in the function `addListeners`

    // imports
    
    export default function Home(props) {
    
      const [web3Modal, setWeb3Modal] = useState(null)
      const [address, setAddress] = useState("")
    
      useEffect(() => {
        // initiate web3modal
        ...
      }, [])
    
      useEffect(() => {
        // connect automatically and without a popup if user is already connected
        if(web3Modal && web3Modal.cachedProvider){
          connectWallet()
        }
      }, [web3Modal])
    
      async function connectWallet() {
        const provider = await web3Modal.connect();
        
        addListeners(provider);
    
        const ethersProvider = new providers.Web3Provider(provider)
        const userAddress = await ethersProvider.getSigner().getAddress()
        setAddress(userAddress)
      }
    
      async function addListeners(web3ModalProvider) {
    
        web3ModalProvider.on("accountsChanged", (accounts) => {
          window.location.reload()
        });
        
        // Subscribe to chainId change
        web3ModalProvider.on("chainChanged", (chainId) => {
          window.location.reload()
        });
      }
    
    
      return (
        <div>
          <button onClick={connectWallet}>Connect wallet</button>
          <p>{address}</p>
        </div>
      )
    }
    

Now the app will automatically reload on a network or an account change. Of course you should customize it to you needs.

Summary
-------

Hopefully this helped you with a problem or gave you a better mindset for building this integration!

If so, you should [follow me on twitter](https://twitter.com/wojtekwtf) or check out some cool stuff that we’re building at [Mazury](https://mazurylabs.com/).

---

*Originally published on [woj.eth](https://paragraph.com/@woj/connecting-to-web3-with-web3modal-react-ethers-js)*
