Multi-wallet linking and switching
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
- The user signs in with an embedded wallet method (for example, Google or email passwordless) through the AUTH connector.
- While authenticated, the user links an external wallet (for example, MetaMask or WalletConnect) to the same account.
- The linked wallet appears in
userInfo.linkedAccounts. - The user switches the active wallet with
switchAccount()or theuseSwitchAccounthook. - The SDK updates
connection.ethereumProviderorconnection.solanaWalletand emits aconnection_updatedevent so Wagmi and your UI resync.
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:
| Field | Description |
|---|---|
id | Linked account identifier |
isPrimary | Whether this is the user's primary account |
eoaAddress | Externally owned account (EOA) address |
aaAddress | Smart account address, if configured |
aaProvider | Smart account provider name |
connector | Connector that owns this account |
active | Whether this account is currently active |
accountType | Account type (for example, social, external_wallet, or account_abstraction) |
address | Account address, or null |
chainNamespace | Chain namespace of the account, or null |
authConnectionId | Auth connection ID of the account, or null |
groupedAuthConnectionId | Grouped auth connection ID of the account, if any |
Link a wallet
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: returnslinkAccount,unlinkAccount,linkedAccounts,loading, anderror.useSwitchAccount: returnsswitchAccount,loading, anderror.
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.
Unlink a wallet
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
- External wallet aggregator: wallet discovery and connect-and-sign flow
- Session management: how sessions persist across reloads
- User details in ID token: configure identity token claims