Connect Wallet Menu

This guide demonstrates one approach to building a wallet connection interface using use-wallet. Being a headless library, use-wallet gives you complete control over your wallet UI implementation - you're free to design the interface that best suits your application's needs.

A Simple Pattern

One straightforward approach is to implement a wallet menu with two distinct states:

  1. Disconnected State: Shows a list of available wallets to choose from

  2. Connected State: Shows details and controls for a single active wallet

While use-wallet supports connecting multiple wallets simultaneously, focusing on a single active wallet can:

  • Simplify the user experience

  • Reduce complexity in state management

  • Make edge cases easier to handle

  • Help avoid potential conflicts between certain wallet providers

This is just one possible pattern - the library's flexible design allows you to implement whatever interface makes sense for your application.

Basic Implementation

Here's a simple implementation that follows this pattern:

import { useWallet, type Wallet } from '@txnlab/use-wallet-react'
import { useState } from 'react'

const WalletMenu = () => {
  const { wallets, activeWallet } = useWallet()
  
  // If we have an active wallet, show the connected view
  if (activeWallet) {
    return <ConnectedWallet wallet={activeWallet} />
  }
  
  // Otherwise, show the wallet selection list
  return <WalletList wallets={wallets} />
}

const WalletList = ({ wallets }: { wallets: Wallet[] }) => {
  return (
    <div className="wallet-list">
      <h3>Connect Wallet</h3>
      <div className="wallet-options">
        {wallets.map((wallet) => (
          <WalletOption
            key={wallet.id}
            wallet={wallet}
          />
        ))}
      </div>
    </div>
  )
}

const WalletOption = ({ wallet }: { wallet: Wallet }) => {
  const [connecting, setConnecting] = useState(false)
  
  const handleConnect = async () => {
    setConnecting(true)
    try {
      await wallet.connect()
    } catch (error) {
      console.error('Failed to connect:', error)
    } finally {
      setConnecting(false)
    }
  }
  
  return (
    <button
      onClick={handleConnect}
      disabled={connecting}
      className="wallet-option"
    >
      <img
        src={wallet.metadata.icon}
        alt={wallet.metadata.name}
        width={32}
        height={32}
      />
      <span>{wallet.metadata.name}</span>
    </button>
  )
}

const ConnectedWallet = ({ wallet }: { wallet: Wallet }) => {
  return (
    <div className="connected-wallet">
      {/* Wallet header */}
      <div className="wallet-header">
        <img
          src={wallet.metadata.icon}
          alt={wallet.metadata.name}
          width={32}
          height={32}
        />
        <span>{wallet.metadata.name}</span>
      </div>
      
      {/* Account selector */}
      {wallet.accounts.length > 1 && (
        <select
          value={wallet.activeAccount?.address}
          onChange={(e) => wallet.setActiveAccount(e.target.value)}
        >
          {wallet.accounts.map((account) => (
            <option key={account.address} value={account.address}>
              {account.name}
            </option>
          ))}
        </select>
      )}
      
      {/* Account details */}
      {wallet.activeAccount && (
        <div className="account-info">
          <span>{wallet.activeAccount.name}</span>
          <span>{wallet.activeAccount.address}</span>
        </div>
      )}
      
      {/* Disconnect button */}
      <button onClick={wallet.disconnect}>
        Disconnect
      </button>
    </div>
  )
}

Error Handling and Loading States

Here's how to implement basic error handling and loading states for wallet interactions:

const WalletOption = ({ wallet }: { wallet: Wallet }) => {
  const [status, setStatus] = useState('idle')
  
  const handleConnect = async () => {
    setStatus('connecting')
    try {
      await wallet.connect()
      setStatus('connected')
    } catch (error) {
      setStatus('error')
      showNotification('Failed to connect wallet')
      console.error('Connection error:', error)
    }
  }
  
  return (
    <button
      onClick={handleConnect}
      disabled={status === 'connecting'}
    >
      {status === 'connecting' ? 'Connecting...' : 'Connect'}
    </button>
  )
}

Accessibility

Ensure your wallet menu is accessible:

<div
  role="dialog"
  aria-labelledby="wallet-menu-title"
  className="wallet-menu"
>
  <h2 id="wallet-menu-title">
    {activeWallet ? 'Connected Wallet' : 'Connect Wallet'}
  </h2>
  
  {/* Menu content */}
</div>

Next Steps

  • Add styling to match your application's design

  • Implement a modal/dropdown container for the menu

  • Add balance display for active account

  • Optional: Add network selection (see Switching Networks)

See Also

Last updated