For AI agents: a documentation index is available at /llms.txt. A markdown version of this page is available at the same URL with .md appended (or via Accept: text/markdown).
Skip to main content

Multi-wallet linking and switching

Web SDK only

Multi-wallet linking and switching ships in Web SDK v11 (@web3auth/modal for JavaScript, React, and Vue).

Users can link multiple external wallets to a single Embedded Wallets account and switch between them during an active session, including across chains. Your dapp receives one canonical user object instead of treating each wallet connection as a separate user.

How it works​

  1. The user signs in with an embedded wallet method (for example, Google or email passwordless) through the AUTH connector.
  2. While authenticated, the user links an external wallet (for example, MetaMask or WalletConnect) to the same account.
  3. The linked wallet appears in userInfo.linkedAccounts.
  4. The user switches the active wallet with switchAccount() or the useSwitchAccount hook.
  5. The SDK updates connection.ethereumProvider or connection.solanaWallet and emits a connection_updated event so Wagmi and your UI resync.
info

Account linking requires an AUTH primary session. You cannot link wallets when the user's only login method is an external wallet connection.

Linked account fields​

Each entry in userInfo.linkedAccounts is a LinkedAccountInfo object:

FieldDescription
idLinked account identifier
isPrimaryWhether this is the user's primary account
eoaAddressExternally owned account (EOA) address
aaAddressSmart account address, if configured
aaProviderSmart account provider name
connectorConnector that owns this account
activeWhether this account is currently active
accountTypeAccount type (for example, social, external_wallet, or account_abstraction)
addressAccount address, or null
chainNamespaceChain namespace of the account, or null
authConnectionIdAuth connection ID of the account, or null
groupedAuthConnectionIdGrouped auth connection ID of the account, if any

Call linkAccount() after the user is connected through the AUTH connector:

import { useWeb3Auth } from '@web3auth/modal/react'
import { WALLET_CONNECTORS } from '@web3auth/modal'

function LinkWalletButton() {
const { web3Auth } = useWeb3Auth()

const handleLink = async () => {
if (!web3Auth) return
const result = await web3Auth.linkAccount({
connectorName: WALLET_CONNECTORS.METAMASK,
})
console.log('Linked accounts:', result.linkedAccounts)
}

return <button onClick={handleLink}>Link MetaMask</button>
}

linkAccount() accepts connectorName and an optional chainId (defaults to the active chain). With @web3auth/modal, connectorName is optional: if you omit it, the modal opens a wallet picker for account linking. The result contains success, a refreshed idToken, the updated linkedAccounts, and an optional message.

Switch the active wallet​

Use switchAccount() with a LinkedAccountInfo entry from userInfo.linkedAccounts:

import { useWeb3Auth, useWeb3AuthUser } from '@web3auth/modal/react'

function WalletSwitcher() {
const { web3Auth } = useWeb3Auth()
const { userInfo } = useWeb3AuthUser()

const handleSwitch = async (accountId: string) => {
if (!web3Auth || !userInfo?.linkedAccounts) return
const account = userInfo.linkedAccounts.find(a => a.id === accountId)
if (!account) return
await web3Auth.switchAccount(account)
}

return (
<ul>
{userInfo?.linkedAccounts?.map(account => (
<li key={account.id}>
{account.eoaAddress}
{account.active ? ' (active)' : ''}
{!account.active && <button onClick={() => handleSwitch(account.id)}>Switch</button>}
</li>
))}
</ul>
)
}

React and Vue hooks​

Import the linking hooks (React) or composables (Vue) from @web3auth/modal/account-linking/react or @web3auth/modal/account-linking/vue:

  • useLinkAccount: returns linkAccount, unlinkAccount, linkedAccounts, loading, and error.
  • useSwitchAccount: returns switchAccount, loading, and error.

useWallets from @web3auth/modal/react or @web3auth/modal/vue returns a wallets list and a syncWallets() function. wallets is empty until you call syncWallets(), and it stays empty until the connection status is authorized.

Remove a linked wallet with unlinkAccount():

await web3Auth.unlinkAccount('<LINKED_EOA_ADDRESS>')

You cannot unlink the primary account or the currently active account.

Why this matters for dapps​

  • Analytics: One user ID maps to every wallet the user owns, not one ID per connection.
  • CRM: Store a single profile with multiple wallet addresses attached.
  • UX: Users can bring a hot wallet, hardware wallet, and side-project wallet under one login without signing out.

Next steps​