# Wallet Connection UX Best Practices
## Comparison of the Three Main Libraries
| Library | Maintainer | Built On | UI Style | Customizability | Recommended Use Case |
|----|--------|------|---------|--------|---------|
| **RainbowKit** | Rainbow | wagmi | Polished, rainbow aesthetic | Medium | General dApps, fast launch |
| **ConnectKit** | Family | wagmi | Clean and modern | High | Brand consistency focused |
| **AppKit (Web3Modal v3)** | WalletConnect | wagmi/ethers | Generic | Medium | Multi-chain, multi-ecosystem |
| **wagmi native** | wagmi | — | No UI | Fully custom | Full customization needed |
---
## RainbowKit (Least Effort)
```bash
npm install @rainbow-me/rainbowkit wagmi viem @tanstack/react-query
```
```tsx
// App.tsx
import '@rainbow-me/rainbowkit/styles.css'
import { getDefaultConfig, RainbowKitProvider } from '@rainbow-me/rainbowkit'
import { WagmiProvider } from 'wagmi'
import { mainnet, polygon, base } from 'wagmi/chains'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const config = getDefaultConfig({
appName: 'My App',
projectId: 'YOUR_WALLETCONNECT_PROJECT_ID', // get from cloud.walletconnect.com
chains: [mainnet, base, polygon],
})
export function App() {
return (
)
}
// Usage
import { ConnectButton } from '@rainbow-me/rainbowkit'
// done in one line
```
**Custom ConnectButton:**
```tsx
{({ account, chain, openConnectModal, openChainModal, mounted }) => (
)}
```
---
## ConnectKit (More Customization)
```bash
npm install connectkit wagmi viem @tanstack/react-query
```
```tsx
import { ConnectKitProvider, ConnectKitButton, getDefaultConfig } from 'connectkit'
const config = createConfig(getDefaultConfig({
walletConnectProjectId: 'YOUR_PROJECT_ID',
appName: 'My App',
chains: [mainnet, base],
}))
// Themes: auto | light | dark | web95 | retro | soft | midnight | minimal | rounded | nouns
```
---
## UX Best Practices
### 1. Show Correct Chain State
```tsx
const { chain } = useAccount()
const { chains, switchChain } = useSwitchChain()
// Detect wrong chain and prompt user to switch
if (chain?.id !== expectedChainId) {
return
}
```
### 2. ENS Name + Avatar Display
```tsx
const { data: ensName } = useEnsName({ address, chainId: mainnet.id })
const { data: avatar } = useEnsAvatar({ name: ensName!, chainId: mainnet.id })
// Show: vitalik.eth instead of 0xd8dA...
const displayName = ensName ?? truncateAddress(address) // "0xd8dA...6045"
```
### 3. Transaction Status Feedback
```tsx
const { writeContract, data: hash, isPending } = useWriteContract()
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
// Three-phase feedback
if (isPending) return Waiting for wallet confirmation...
if (isConfirming) return Confirming transaction (0/{targetConfirmations})
if (isSuccess) return ✅ Transaction successful!
```
### 4. Gas Estimation
```tsx
import { useEstimateGas } from 'wagmi'
const { data: gasEstimate } = useEstimateGas({ to, data, value })
// Display estimated gas cost
```
### 5. Mobile Compatibility
- RainbowKit supports WalletConnect QR and mobile deep links out of the box
- Prioritize MetaMask Mobile / Trust Wallet when a mobile device is detected
- iOS Safari note: wallet actions must be triggered by a user gesture (cannot open automatically)
## Common Pitfalls
- `projectId` must be obtained from cloud.walletconnect.com — do not use the example ID
- ENS lookups only work on Ethereum Mainnet (chainId=1); other chains return null
- Do not use wagmi hooks directly in SSR — wrap them inside a client component
# Wallet Connection UX Best Practices
## Comparison of the Three Main Libraries
| Library | Maintainer | Built On | UI Style | Customizability | Recommended Use Case |
|----|--------|------|---------|--------|---------|
| **RainbowKit** | Rainbow | wagmi | Polished, rainbow aesthetic | Medium | General dApps, fast launch |
| **ConnectKit** | Family | wagmi | Clean and modern | High | Brand consistency focused |
| **AppKit (Web3Modal v3)** | WalletConnect | wagmi/ethers | Generic | Medium | Multi-chain, multi-ecosystem |
| **wagmi native** | wagmi | — | No UI | Fully custom | Full customization needed |
---
## RainbowKit (Least Effort)
```bash
npm install @rainbow-me/rainbowkit wagmi viem @tanstack/react-query
```
```tsx
// App.tsx
import '@rainbow-me/rainbowkit/styles.css'
import { getDefaultConfig, RainbowKitProvider } from '@rainbow-me/rainbowkit'
import { WagmiProvider } from 'wagmi'
import { mainnet, polygon, base } from 'wagmi/chains'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const config = getDefaultConfig({
appName: 'My App',
projectId: 'YOUR_WALLETCONNECT_PROJECT_ID', // get from cloud.walletconnect.com
chains: [mainnet, base, polygon],
})
export function App() {
return (
)
}
// Usage
import { ConnectButton } from '@rainbow-me/rainbowkit'
// done in one line
```
**Custom ConnectButton:**
```tsx
{({ account, chain, openConnectModal, openChainModal, mounted }) => (
)}
```
---
## ConnectKit (More Customization)
```bash
npm install connectkit wagmi viem @tanstack/react-query
```
```tsx
import { ConnectKitProvider, ConnectKitButton, getDefaultConfig } from 'connectkit'
const config = createConfig(getDefaultConfig({
walletConnectProjectId: 'YOUR_PROJECT_ID',
appName: 'My App',
chains: [mainnet, base],
}))
// Themes: auto | light | dark | web95 | retro | soft | midnight | minimal | rounded | nouns
```
---
## UX Best Practices
### 1. Show Correct Chain State
```tsx
const { chain } = useAccount()
const { chains, switchChain } = useSwitchChain()
// Detect wrong chain and prompt user to switch
if (chain?.id !== expectedChainId) {
return
}
```
### 2. ENS Name + Avatar Display
```tsx
const { data: ensName } = useEnsName({ address, chainId: mainnet.id })
const { data: avatar } = useEnsAvatar({ name: ensName!, chainId: mainnet.id })
// Show: vitalik.eth instead of 0xd8dA...
const displayName = ensName ?? truncateAddress(address) // "0xd8dA...6045"
```
### 3. Transaction Status Feedback
```tsx
const { writeContract, data: hash, isPending } = useWriteContract()
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
// Three-phase feedback
if (isPending) return Waiting for wallet confirmation...
if (isConfirming) return Confirming transaction (0/{targetConfirmations})
if (isSuccess) return ✅ Transaction successful!
```
### 4. Gas Estimation
```tsx
import { useEstimateGas } from 'wagmi'
const { data: gasEstimate } = useEstimateGas({ to, data, value })
// Display estimated gas cost
```
### 5. Mobile Compatibility
- RainbowKit supports WalletConnect QR and mobile deep links out of the box
- Prioritize MetaMask Mobile / Trust Wallet when a mobile device is detected
- iOS Safari note: wallet actions must be triggered by a user gesture (cannot open automatically)
## Common Pitfalls
- `projectId` must be obtained from cloud.walletconnect.com — do not use the example ID
- ENS lookups only work on Ethereum Mainnet (chainId=1); other chains return null
- Do not use wagmi hooks directly in SSR — wrap them inside a client component
- Handle chain-switch failures (user rejection) gracefully with a friendly message — don't crash