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

Web SDK v11 Migration Guide

This guide upgrades Embedded Wallets Web SDK integrations from v9 or v10 directly to v11 for JavaScript (@web3auth/modal), React (@web3auth/modal/react), and Vue (@web3auth/modal/vue).

Platform scope

This guide applies to the Web SDK only. Android, iOS, React Native, Flutter, Unity, and Unreal SDKs follow separate version lines and migration guides.

AI-assisted migration

For the best results, install the MetaMask Embedded Wallets skill and MCP server before you migrate. See Build with AI for setup (npx skills add web3auth/skill and MCP at https://mcp.web3auth.io).

Copy the prompt below into your AI coding assistant (Cursor, Claude Code, Codex, Antigravity, or similar):

Migrate my MetaMask Embedded Wallets (@web3auth/modal) project to v11.

Before changing code:
1. Use the web3auth skill and MCP tools (search_docs, get_doc, get_example, get_sdk_reference).
2. Read the migration guide: https://docs.metamask.io/embedded-wallets/migration-guides/web
3. Detect my framework (JavaScript, React, or Vue) and my current SDK version (v9 or v10).

Then migrate my codebase directly to v11:
- If on v9 modal: apply v9 breaking changes (dashboard chains, connectTo, hooks paths) and v11 changes (connection, Solana kit, polyfills) in one pass.
- If on v9 no-modal: migrate to @web3auth/modal@11, replace Web3AuthNoModal/adapters with connectTo() and dashboard connections, then apply v11 provider/Solana changes.
- If on v10: apply v11 changes only (connection, Solana kit, polyfills, wagmi v3).
- Update package.json dependencies and remove deprecated packages.
- Replace web3auth.provider with connection.ethereumProvider where needed.
- For Solana apps: migrate from @solana/web3.js to @solana/kit and update hook/composable import paths.
- Remove buffer/process/global polyfills added for older SDK versions.
- Do not change my Client ID or Sapphire network unless I ask; that would change wallet addresses.

After migrating, list every file you changed and any manual dashboard steps I still need to do.
tip

Use planning mode (where available) for the initial prompt. Review the plan before generating code; config mistakes can change wallet addresses in production.

Install v11

npm install @web3auth/modal@11

From v9 no-modal: uninstall legacy packages first, then install the unified SDK:

npm uninstall @web3auth/no-modal @web3auth/auth-adapter @web3auth/wallet-services-plugin
npm install @web3auth/modal@11

Remove other deprecated adapter and plugin packages (see framework tabs below).

For custom blockchain configurations that still need a private key provider in code, you may still install @web3auth/ethereum-provider, but prefer dashboard chain configuration for standard EVM chains.

For Solana apps, also install:

npm install @solana/kit @solana-program/system @solana/react-hooks

@solana/react-hooks is a required peer dependency for Solana integrations with @web3auth/modal.

React and Vue apps using EVM with Wagmi should upgrade to Wagmi v3:

npm install wagmi@3 @tanstack/react-query

For Vue, also register Vue Query in main.ts:

import { VueQueryPlugin } from '@tanstack/vue-query'

createApp(App).use(VueQueryPlugin).mount('#app')

Migrating from v9

If you're on v9, apply the sections below together with v11 API changes. Version 10 consolidated modal and no-modal flows into @web3auth/modal, moved configuration to the Embedded Wallets dashboard, and integrated Wallet Services and smart accounts into the main SDK; v11 keeps that model and adds the connection API and Solana kit migration described later.

No-modal SDK (v9)

Recommended: use @web3auth/modal

While @web3auth/no-modal received updates in v9, v11 uses @web3auth/modal for custom UI flows. The modal package supports both the pre-built UI (connect()) and headless custom UI (connectTo()).

Package and class migration

v9

import { Web3AuthNoModal } from '@web3auth/no-modal'
import { AuthAdapter } from '@web3auth/auth-adapter'

const web3auth = new Web3AuthNoModal({
clientId: 'YOUR_CLIENT_ID',
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
chainConfig,
privateKeyProvider,
})

v11

import { Web3Auth, WEB3AUTH_NETWORK } from '@web3auth/modal'

const web3auth = new Web3Auth({
clientId: 'YOUR_CLIENT_ID',
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
})

Custom UI with connectTo

v9

const authAdapter = new AuthAdapter({
adapterSettings: {
loginConfig: {
google: {
verifier: 'YOUR_GOOGLE_VERIFIER_NAME',
typeOfLogin: 'google',
clientId: 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',
},
},
},
})

web3auth.configureAdapter(authAdapter)
await web3auth.init()

await web3auth.connectTo('auth', {
loginProvider: 'google',
})

v11

import { AUTH_CONNECTION, WALLET_CONNECTORS, WEB3AUTH_NETWORK, Web3Auth } from '@web3auth/modal'

await web3auth.init()

await web3auth.connectTo(WALLET_CONNECTORS.AUTH, {
authConnection: AUTH_CONNECTION.GOOGLE,
})

// Or with a dashboard connection ID
await web3auth.connectTo(WALLET_CONNECTORS.AUTH, {
authConnection: AUTH_CONNECTION.GOOGLE,
authConnectionId: 'YOUR_GOOGLE_AUTH_CONNECTION_ID',
})

Verifiers and aggregate verifiers from v9 map to dashboard connections. See Custom authentication.

After sign-in, read the EVM provider from web3auth.connection?.ethereumProvider (see v11 API changes).

Remove packages such as @web3auth/openlogin-adapter and @web3auth/wallet-services-plugin. Wallet Services is built into @web3auth/modal.

If your no-modal init passed chainConfig and EthereumPrivateKeyProvider, remove them and use dashboard chain configuration instead.

General changes (all frameworks)

Centralized chain configuration

Remove EthereumPrivateKeyProvider, chainConfig, and privateKeyProvider from Web3AuthOptions for standard EVM chains. Configure chains on the Embedded Wallets dashboard.

import { Web3Auth, WEB3AUTH_NETWORK } from '@web3auth/modal'

const web3auth = new Web3Auth({
clientId: WEB3AUTH_CLIENT_ID,
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
})

Multi-Factor Authentication (MFA)

Move MFA settings from AuthAdapter to Web3AuthOptions:

import { MFA_FACTOR, MFA_LEVELS, WEB3AUTH_NETWORK, type Web3AuthOptions } from '@web3auth/modal'

const web3AuthOptions: Web3AuthOptions = {
clientId: 'YOUR_CLIENT_ID',
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
mfaLevel: MFA_LEVELS.MANDATORY,
mfaSettings: {
[MFA_FACTOR.DEVICE]: { enable: true, priority: 1, mandatory: true },
[MFA_FACTOR.BACKUP_SHARE]: { enable: true, priority: 2, mandatory: true },
},
}

Method renames

v9v11
useCoreKitKey: trueuseSFAKey: true
web3auth.authenticateUser()web3auth.getIdentityToken()

Wallet Services, whitelabeling, smart accounts, custom auth

v9 approachv11 approach
@web3auth/wallet-services-pluginBuilt into @web3auth/modal; see Wallet Services
uiConfig / adapter whitelabelDashboard customization; see Whitelabel
@web3auth/account-abstraction-providerDashboard-managed smart accounts; see Smart accounts
Verifiers / aggregate verifiersDashboard connections; see Custom authentication

Framework-specific changes

Use @web3auth/modal for both modal and custom UI flows.

Modal UI (v9):

import { Web3Auth, WEB3AUTH_NETWORK } from '@web3auth/modal'

const web3auth = new Web3Auth({
clientId: WEB3AUTH_CLIENT_ID,
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
})

await web3auth.init()
await web3auth.connect()

const provider = web3auth.provider

v11 (modal UI):

await web3auth.init()
await web3auth.connect()

const provider = web3auth.connection?.ethereumProvider ?? null

Custom UI (v9 no-modal): see No-modal SDK (v9) for connectTo() and connection ID migration. After connectTo(), use web3auth.connection?.ethereumProvider.

v11 API changes

Web SDK v11 centers on two breaking themes:

  1. Provider surface removed from the public APIuseWeb3Auth() no longer returns provider, and reading web3auth.provider on the Web3Auth class returns undefined. Use the connection object instead.
  2. Solana SDK replaced@solana/web3.js is no longer the primary RPC surface. Use @solana/kit, @solana-program/system, and Solana hooks from @web3auth/modal/react/solana or @web3auth/modal/vue/solana.

If you're already on v10, this section is the main code update. If you're on v9, apply it together with Migrating from v9.

Provider API changes (all frameworks)

useWeb3Auth() no longer returns provider

v10

const { provider } = useWeb3Auth()
const accounts = await provider.request({ method: 'eth_accounts' })

v11

const { connection } = useWeb3Auth()
// connection: { ethereumProvider, solanaWallet, connectorName }

connection is the v11 public surface. Reach ethereumProvider only when no hook answers your question. Use it for:

  • Extracting a raw private key for non-EVM chains
  • Custom JSON-RPC methods (for example, xrpl_*)
  • Wrapping in ethers.BrowserProvider for libraries that require an EIP-1193 provider
  • Fetching the app public key for social-login server-side verification

For EVM work in React and Vue, prefer Wagmi hooks (useAccount, useBalance, useSendTransaction, useSignMessage). They receive the provider through the Web3Auth connector and do not need manual wiring.

Web3Auth class: use connection.ethereumProvider instead of provider

In v11, provider on the Web3Auth class is a setter only. Reading web3auth.provider at runtime returns undefined. Use web3auth.connection?.ethereumProvider instead.

v10

const provider = web3auth.provider

v11

const provider = web3auth.connection?.ethereumProvider ?? null

When you still need connection.ethereumProvider

Use it only for JSON-RPC edge cases Wagmi does not cover:

  • private_key for non-EVM chain derivation
  • public_key for social-login server verification
  • Custom JSON-RPC methods

For external wallets, use Wagmi useAccount().address instead of eth_accounts.

const pubKey = await connection?.ethereumProvider?.request({ method: 'public_key' })

Migration recipes (v10 → v11)

Use the recipe that matches your app. See Provider API changes for the underlying API shift.

Recipe A: EVM apps using Wagmi only (no change)

If your app already uses Wagmi hooks from @web3auth/modal/react/wagmi or @web3auth/modal/vue/wagmi and never reads useWeb3Auth().provider, you may not need EVM code changes beyond the Wagmi v3 upgrade.

// No changes needed — Wagmi hooks receive the provider automatically
const { address } = useAccount()
const { data: balance } = useBalance({ address })

Recipe B: Non-EVM chains that need the private key (React)

Chains such as Algorand, Aptos, Cosmos, Polkadot, Starknet, Sui, and Tron often need the raw private key. Extract it once in a useEffect, store it in state, and pass the string to helper functions.

v10 RPC helper

export async function getAccounts(provider: IProvider): Promise<string> {
const privateKey = await provider.request({ method: 'private_key' })
// ... use privateKey
}

v11 RPC helper — takes privateKey: string directly; no IProvider import in the helper

export async function getAccounts(privateKey: string): Promise<string> {
// ... use privateKey
}

v10 component

const { provider } = useWeb3Auth()

const onGetAccounts = async () => {
const accounts = await getAccounts(provider)
}

v11 component

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

const { connection } = useWeb3Auth()
const [privateKey, setPrivateKey] = useState<string | null>(null)

useEffect(() => {
if (!connection?.ethereumProvider) return
;(connection.ethereumProvider.request({ method: 'private_key' }) as Promise<string>)
.then(k => setPrivateKey(k ?? null))
.catch(console.error)
}, [connection])

const onGetAccounts = async () => {
if (!privateKey) return
await getAccounts(privateKey)
}

See connect-blockchain for chain-specific helpers.

Recipe C: Libraries that require IProvider (XMTP, XRPL, multi-chain)

When a library needs an ethers.BrowserProvider wrapper or custom RPC methods, keep IProvider in helper signatures and source it from connection at each call site.

v10

const { provider } = useWeb3Auth()
const accounts = await getAccounts(provider)

v11

const { connection } = useWeb3Auth()

const onGetAccounts = async () => {
const provider = connection?.ethereumProvider ?? null
if (!provider) return
await getAccounts(provider)
}

Do not rely on a global "provider ready" flag. Guard each call with if (!provider) return.

Recipe D: Vanilla JS and Angular (no hooks)

v10

const provider = web3auth.provider

v11

function getProvider() {
return web3auth.connection?.ethereumProvider ?? null
}

async function getAccounts() {
const provider = getProvider()
if (!provider) return
const address = await RPC.getAccounts(provider)
}

Recipe E: Server-side verification (social vs external wallet)

v10 (external wallet anti-pattern)

const { web3Auth } = useWeb3Auth()
const { address } = useAccount()

if (connectorName === 'auth') {
const pubKey = await web3Auth?.provider?.request({ method: 'public_key' })
} else {
// Redundant — fetches the same address Wagmi already exposes
const address = await web3Auth?.provider?.request({ method: 'eth_accounts' })
}

v11

const { connection } = useWeb3Auth()
const { address } = useAccount()

if (connectorName === 'auth') {
// Social login: no Wagmi hook exposes the app public key
const pubKey = await connection?.ethereumProvider?.request({ method: 'public_key' })
// body: JSON.stringify({ appPubKey: pubKey })
} else {
// External wallet: use Wagmi address
// body: JSON.stringify({ address })
}

Solana migration (all frameworks)

Remove @solana/web3.js as the primary dependency. Add @solana/kit, @solana-program/system, and @solana/react-hooks (see Install v11).

Hooks import path

v10

import { useSolanaWallet } from '@web3auth/modal/react'

v11

import {
useSolanaWallet,
useSignTransaction,
useSignAndSendTransaction,
useSignMessage,
} from '@web3auth/modal/react/solana'

For Vue, import from @web3auth/modal/vue/solana.

useSolanaWallet() return shape

v10 returned a @solana/web3.js Connection object.

v11 returns:

{
accounts: string[] | null
solanaWallet: Wallet | null
rpc: Rpc<SolanaRpcApi> | null
getPrivateKey: () => Promise<string>
}

Fetch balance

v10

import { Connection, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js'
const connection = new Connection(clusterApiUrl('devnet'))
const balance = await connection.getBalance(new PublicKey(account))
const sol = balance / LAMPORTS_PER_SOL

v11

import { address } from '@solana/kit'
import { useSolanaWallet } from '@web3auth/modal/react/solana'

const { accounts, rpc } = useSolanaWallet()
const { value } = await rpc!.getBalance(address(accounts![0])).send()
const sol = Number(value) / 1e9

In Vue, unwrap refs with .value (rpc.value, accounts.value![0]).

Build and sign a transfer

v11 uses @solana/kit with the pipe builder and getTransferSolInstruction from @solana-program/system:

import {
address,
appendTransactionMessageInstruction,
compileTransaction,
createNoopSigner,
createTransactionMessage,
lamports,
pipe,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
} from '@solana/kit'
import { getTransferSolInstruction } from '@solana-program/system'
import { useSolanaWallet, useSignTransaction } from '@web3auth/modal/react/solana'

const { accounts, rpc } = useSolanaWallet()
const { signTransaction } = useSignTransaction()

const { value: latestBlockhash } = await rpc!.getLatestBlockhash().send()
const feePayer = createNoopSigner(address(accounts![0]))

const message = pipe(
createTransactionMessage({ version: 0 }),
m => setTransactionMessageFeePayerSigner(feePayer, m),
m => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
m =>
appendTransactionMessageInstruction(
getTransferSolInstruction({
source: feePayer,
destination: address(toAddress),
amount: lamports(BigInt(Math.floor(amount * 1e9))),
}),
m
)
)

signTransaction(compileTransaction(message))

Key differences from v10:

  • The fee payer must be a TransactionSigner, not a bare PublicKey. Use createNoopSigner(address(pubkey)).
  • Pass the same feePayer signer as source in getTransferSolInstruction.
  • useSignTransaction and useSignAndSendTransaction accept the compiled Transaction from @solana/kit.

See React Solana hooks or Vue Solana composables for full transaction flows.

Sign a message

v10

const encodedMessage = new TextEncoder().encode('Hello, Web3Auth!')
const signedMessage = await solanaWallet.signMessage(new Uint8Array(encodedMessage))

v11

import { useSignMessage } from '@web3auth/modal/react/solana'

const { signMessage } = useSignMessage()
const message = new TextEncoder().encode('Hello, Web3Auth!')
signMessage(message)

Optional: keep @solana/web3.js as a narrow bridge

Some third-party libraries (@bonfida/spl-name-service, @solana/pay) still depend on @solana/web3.js types. Keep the package installed but limit usage to the component that calls the library. Build transactions with @solana/kit everywhere else.

For Vanilla JS, read connection.solanaWallet from web3auth.connection after sign-in. See the JavaScript SDK Solana integration snippets.

Polyfill removal

v11 no longer needs browser polyfills. Remove the following from every project.

Vite (vite.config.ts or vite.config.js)

Delete the define block and any commented browserify resolve.alias entries:

- define: {
- global: 'globalThis',
- },

Removable packages

Remove these from dependencies or devDependencies wherever present:

PackagePolyfilled Node.js API
bufferBuffer
processprocess
crypto-browserifycrypto
stream-browserifystream
assertassert
os-browserifyos
stream-httphttp
https-browserifyhttps
empty-moduleUnused module stub
urlurl
zlib-browserifyzlib

Angular (src/polyfills.ts)

Remove global, Buffer, and process shims. Keep only zone.js:

import 'zone.js'
-
- (window as any).global = window
- global.Buffer = global.Buffer || require('buffer').Buffer
- global.process = global.process || require('process')

Framework-specific v11 changes

After connect(), read the EVM provider from connection:

await web3auth.connect()
const provider = web3auth.connection?.ethereumProvider ?? null

For Solana, use web3auth.connection?.solanaWallet instead of wrapping web3auth.provider in SolanaWallet.

Breaking changes in v11

Review these changes when upgrading from v10.

If you hit SDK bugs during upgrade, report them on builder.metamask.io and check web3auth-web release notes for fixes and known issues.

Node.js minimum version

@web3auth/modal@11 requires Node.js 22+ and npm 10+. Support for Node.js 18 and 20 is discontinued.

node --version # must be >= 22.x

Dedicated social login connector removed

The standalone social login connector is consolidated into the unified modal flow. Configure social providers through the Embedded Wallets dashboard instead of a separate connector package.

Themed context removed

The ThemedContext pattern from earlier SDK versions is removed. Use the modal state pattern and dashboard Customization settings for branding instead.

External wallet authentication mode

External wallet login defaults to connect and sign (initialAuthenticationMode: 'connect-and-sign'). Sessions persist across page reloads without re-prompting for a signature. See External wallet aggregator.

New Web SDK feature pages

FeatureDocumentation
Multi-wallet linking and switchingMulti-wallet linking
EIP-7702 smart accountsSmart accounts
Role-based access controlAccess control
Consent screenConsent screen

Summary table

v10 → v11 quick reference

Areav10v11
Provider from hookconst { provider } = useWeb3Auth()Use connection or Wagmi hooks
Provider from classweb3auth.providerweb3auth.connection?.ethereumProvider
EVM in React/Vueprovider.request(...)Wagmi hooks (useAccount, useBalance, etc.)
Raw private keyprovider.request({ method: 'private_key' })Once via connection.ethereumProvider in useEffect, then pass string to helpers
Solana RPC clientnew Connection(...) from @solana/web3.jsrpc from useSolanaWallet()
Solana transactionnew Transaction(), SystemProgram.transfer(...)pipe(createTransactionMessage(...), ...) + getTransferSolInstruction
Solana fee payernew PublicKey(address)createNoopSigner(address(pubkey))
Solana signing hooks@web3auth/modal/react@web3auth/modal/react/solana
Server pubkey (social)web3Auth?.provider?.request({ method: 'public_key' })connection?.ethereumProvider?.request({ method: 'public_key' })
Server address (external)provider?.request({ method: 'eth_accounts' })useAccount().address from Wagmi

v9, v10, and v11

Areav9v10v11
Package@web3auth/modal or no-modal + adapters@web3auth/modal@web3auth/modal@11
Custom UI loginconnectTo('auth', { loginProvider }) + adaptersconnectTo(WALLET_CONNECTORS.AUTH, { authConnection })Same as v10
Chain configCode (chainConfig)DashboardDashboard
Provider from hookproviderproviderconnection or Wagmi
Provider from classweb3auth.providerweb3auth.providerweb3auth.connection?.ethereumProvider
Identity tokenauthenticateUser()getIdentityToken()getIdentityToken()
Solana RPC@solana/web3.js Connection@solana/web3.js Connectionrpc from useSolanaWallet()
Solana hooks path@web3auth/modal/react@web3auth/modal/react@web3auth/modal/react/solana
PolyfillsOften requiredOften requiredNot required
Node.js18+20+22+ (required)
External wallet authConnect then signConnect then signConnect and sign (default)
Multi-wallet linkingNot availableNot availablelinkAccount, switchAccount

Next steps