Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

ConnectWallet

Wallet connection button with a built-in 5-state machine: disconnected, connecting, connected, wrong chain, and error. Handles ENS resolution, balance display, chain switching, and account dropdown.

Bundle: 4.8 kB JS (gzip) + 2.1 kB CSS (gzip) added to your app.

Import

import { ConnectWallet } from '@txkit/react'

Usage

<ConnectWallet />

Props

PropTypeDefault
classNamestring-
labelstring'Connect Wallet'
chainIdnumber-
labelsPartial<ConnectWalletLabels>See Labels
size'default' | 'compact''default'
variant'default' | 'outline' | 'ghost' | 'soft''default'
showBalancebooleantrue
showAvatarbooleantrue
showEnsbooleantrue
showFiatbooleanfalse
showChainSelectorbooleantrue
avatarStyle'gradient' | 'pixel''gradient'
onConnect(data: { address: string; connector: string }) => void-
onDisconnect() => void-
onError(error: Error) => void-
onRequestConnect() => boolean | void-
formatAddress(address: string, ensName?: string) => string-
  • chainId - When set, forces the user to be on this chain. If they are on a different chain, the button shows "Wrong Network" and clicking it triggers a chain switch.
  • onRequestConnect - Intercept the connect button click. Return true to skip the built-in wallet selector modal - useful when delegating to RainbowKit's useConnectModal, Reown AppKit, or any custom wallet picker.
  • formatAddress - Custom address formatting function. Receives the raw address and resolved ENS name (if available). Return value is displayed in the button.
  • labels - Override any UI text string. See Labels below.

States

useWalletState is a state machine with 5 states (the WalletState type union):

StateButton ShowsOn Click
disconnectedLabel textOpens wallet selection modal
connectingSpinner + connecting wallet nameDisabled
connectedAddress / ENS + balanceOpens account dropdown
wrong-chain"Wrong Network"Triggers chain switch
error"Try Again"Opens wallet selection modal

The default <ConnectWallet /> UI also exposes a separate data-state="initializing" attribute on the root element while the connector list is empty (typically the first frame after TxKitProvider mounts and autoConnect is hydrating). This is a UI affordance for skeleton placeholders, not part of the public WalletState union - custom-render consumers branch on state and treat the brief gap as 'disconnected' (because address is still undefined).

Examples

Custom Label

<ConnectWallet label="Sign In" />

With Callbacks

<ConnectWallet
  onConnect={(data) => console.log('Connected:', data.address)}
  onDisconnect={() => console.log('Disconnected')}
  onError={(error) => console.error(error.message)}
/>

Custom Address Format

<ConnectWallet
  formatAddress={(address) => `${address.slice(0, 6)}..${address.slice(-2)}`}
/>

Force Chain

import { arbitrum } from 'viem/chains'
 
<ConnectWallet chainId={arbitrum.id} />

Hide Balance and Avatar

<ConnectWallet showBalance={false} showAvatar={false} />

Wallet Kit Integration (RainbowKit / AppKit / ConnectKit)

ConnectWallet ships its own wallet selector modal, but you can delegate the wallet picking step to an existing wallet kit (RainbowKit, Reown AppKit, ConnectKit, Privy, Dynamic) while keeping the txKit button visual language and the rest of the dApp's wagmi state unified.

The hook is the onRequestConnect prop - return true from it to suppress the built-in modal. Anything that already calls useAccount / useConnect keeps working because all kits sit on the same wagmi context.

RainbowKit

import { TxKitProvider, ConnectWallet } from '@txkit/react'
import { RainbowKitProvider, useConnectModal } from '@rainbow-me/rainbowkit'
import '@rainbow-me/rainbowkit/styles.css'
import { mainnet } from 'viem/chains'
import { http } from 'viem'
 
const ConnectButton = () => {
  const { openConnectModal } = useConnectModal()
 
  return (
    <ConnectWallet
      onRequestConnect={() => {
        if (!openConnectModal) {
          return false
        }
        openConnectModal()
        return true
      }}
    />
  )
}
 
const App = () => (
  <TxKitProvider config={{
    chains: [ mainnet ],
    transports: { [mainnet.id]: http() },
  }}>
    <RainbowKitProvider modalSize="compact" locale="en">
      <ConnectButton />
    </RainbowKitProvider>
  </TxKitProvider>
)

TxKitProvider owns the wagmi store and RainbowKitProvider reuses it - do not create a second wagmi config. Set locale="en" explicitly, RainbowKit defaults to the browser locale and will warn loudly without it.

If your app already runs RainbowKit at the root, flip the wrapping order and switch to embedded mode - see Embedded Mode:

<WagmiProvider config={yourWagmiConfig}>
  <QueryClientProvider client={queryClient}>
    <RainbowKitProvider locale="en">
      <TxKitProvider embedded>
        <ConnectButton />
      </TxKitProvider>
    </RainbowKitProvider>
  </QueryClientProvider>
</WagmiProvider>

Reown AppKit

import { useAppKit } from '@reown/appkit/react'
 
const ConnectButton = () => {
  const { open } = useAppKit()
 
  return (
    <ConnectWallet
      onRequestConnect={() => {
        open({ view: 'Connect' })
        return true
      }}
    />
  )
}

ConnectKit

import { useModal } from 'connectkit'
 
const ConnectButton = () => {
  const { setOpen } = useModal()
 
  return (
    <ConnectWallet
      onRequestConnect={() => {
        setOpen(true)
        return true
      }}
    />
  )
}

Choosing the right composition

Your starting pointRecommended setup
Greenfield projectTxKitProvider standalone, txKit's built-in modal
Greenfield, want RainbowKit/AppKit modal stylingTxKitProvider standalone + wallet kit provider as child + onRequestConnect
Existing app on RainbowKit / AppKit / ConnectKit / wagmiTxKitProvider embedded inside the existing wagmi tree

In all three setups, ConnectWallet, TokenBalance, TransactionButton, and the flow components share one wagmi store - no duplicate connectors, no duplicate balance fetches, no nested provider warnings.

Custom Render

Use the children render function for full UI control. The render function receives the full state machine - branch on state to render every phase.

<ConnectWallet>
  {({ state, displayAddress, ensAvatar, openModal, disconnect, switchChain, requiredChain, error, connectingWallet }) => {
    if (state === 'connecting') {
      return (
        <button disabled>
          Connecting to {connectingWallet ?? 'wallet'}...
        </button>
      )
    }
    if (state === 'wrong-chain' && requiredChain) {
      return (
        <button onClick={() => switchChain(requiredChain.id)}>
          Switch to {requiredChain.name}
        </button>
      )
    }
    if (state === 'error') {
      return (
        <button onClick={openModal} aria-invalid="true">
          {error?.message ?? 'Connection failed'} - retry
        </button>
      )
    }
    if (state === 'connected') {
      return (
        <button onClick={disconnect}>
          {ensAvatar ? <img src={ensAvatar} alt="" /> : null}
          {displayAddress}
        </button>
      )
    }
    return <button onClick={openModal}>Sign In</button>
  }}
</ConnectWallet>

Render Data

FieldTypeDescription
stateWalletStateCurrent connection state
address`0x${string}` | undefinedConnected wallet address
displayAddressstring | undefinedShortened address or ENS name
ensNamestring | null | undefinedENS name
ensAvatarstring | null | undefinedENS avatar URL
formattedBalancestring | undefinedNative balance with symbol (e.g. "1.23 ETH")
fiatBalancestring | undefinedFiat-formatted balance (e.g. "$3,456.78")
chainChain | undefinedCurrent chain
requiredChainChain | undefinedRequired chain when chainId prop is set
chainsreadonly Chain[]Available chains for switching
connectorsreadonly Connector[]Available wallet connectors
connectingWalletstring | undefinedName of wallet currently being connected
connect(connector: Connector) => voidConnect a specific wallet
disconnect() => voidDisconnect and close panel
switchChain(chainId: number) => voidSwitch to a different chain
openModal() => voidOpen wallet selection modal
closePanel() => voidClose any open panel
errorError | nullConnection error
isPendingbooleanTrue while connection is pending
isTimedOutbooleanTrue when connection has exceeded timeout
isWrongChainbooleanTrue when chainId is enforced and current chain differs

Hook

useWalletState

Headless hook for wallet connection state. Use this for complete UI control without the ConnectWallet component.

import { useWalletState } from '@txkit/react'
 
const MyWalletButton = () => {
  const { state, displayAddress, connect, connectors, disconnect } = useWalletState()
 
  if (state === 'connected') {
    return <button onClick={disconnect}>{displayAddress}</button>
  }
 
  return (
    <div>
      {connectors.map((connector) => (
        <button key={connector.uid} onClick={() => connect({ connector })}>
          {connector.name}
        </button>
      ))}
    </div>
  )
}
OptionTypeDefaultDescription
chainIdnumber-Force specific chain. Shows wrong-chain state if mismatch.
showBalancebooleantrueFetch and display native balance.
showEnsbooleantrueResolve ENS name and avatar.

Returns the same fields as Render Data above (except openModal and closePanel which are component-specific).

Labels

Override UI text by passing a partial ConnectWalletLabels object:

<ConnectWallet
  labels={{
    connect: 'Sign In',
    connecting: 'Signing in...',
    disconnect: 'Sign Out',
  }}
/>

All Label Keys

{chain} and {wallet} placeholders are interpolated at runtime.

KeyDefaultWhere
connect'Connect Wallet'Button - disconnected
connecting'Connecting'Button - connecting
wrongChain'Wrong Network'Button - wrong chain
switchChain'Switch Network'Button - chain switch action
switchTo'Switch to {chain}'Mismatch banner row
disconnect'Disconnect'Dropdown menu
copyAddress'Copy Address'Dropdown menu
copied'Copied!'Post-copy feedback
error'Connection Failed'Button - error state
retry'Try Again'Button - retry
selectWallet'Select Wallet'Modal title
whatIsWallet'What is a wallet?'Modal info link
explorer'Explorer'Dropdown menu
searchWallets'Search wallets...'Modal search input
installedWallets'Installed'Modal group header
recentWallets'Recent'Modal group header
popularWallets'Popular'Modal group header
allWallets'All Wallets'Modal group header
openingWallet'Opening {wallet}...'Connecting detail
approveInWallet'Approve in {wallet}'Connecting detail
takingTooLong'Taking too long?'Timeout hint
tryDifferent'Try a different wallet'Timeout link
switchNetwork'Switch Network'Chain selector title
unknownChain'Unknown Network'Fallback for unknown chain id
scanWithPhone'Scan with your phone'QR code instruction
copyLink'Copy Link'WalletConnect URI button
connectingTo'Connecting to'Anti-phishing dApp origin prefix