Migrate from v1 to v2
Overview
Wagmi v2 redesigns the core APIs to mesh better with Viem and TanStack Query. This major version transforms Wagmi into a light wrapper around these libraries, sprinkling in multichain support and account management. As such, there are some breaking changes and deprecations to be aware of outlined in this guide.
To get started, install the latest version of Wagmi and it's required peer dependencies.
pnpm add wagmi viem@2.x @tanstack/react-query
npm install wagmi viem@2.x @tanstack/react-query
yarn add wagmi viem@2.x @tanstack/react-query
bun add wagmi viem@2.x @tanstack/react-query
Wagmi v2 should be the last major version that will have this many actionable breaking changes.
Moving forward after Wagmi v2, new functionality will be opt-in with old functionality being deprecated alongside the new features. This means upgrading to the latest major versions will not require immediate changes.
Not ready to migrate yet?
The Wagmi v1 docs are still available at 1.x.wagmi.sh/react.
Dependencies
Moved TanStack Query to peer dependencies
Wagmi uses TanStack Query to manage async state, handling requests, caching, and more. With Wagmi v1, TanStack Query was an internal implementation detail. With Wagmi v2, TanStack Query is a peer dependency. A lot of Wagmi users also use TanStack Query in their apps so making it a peer dependency gives them more control and removes some custom Wagmi code internally.
If you don't normally use TanStack Query, all you need to do is set it up and mostly forget about it (we'll provide guidance around version updates).
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider } from 'wagmi'
import { config } from './config'
const queryClient = new QueryClient()
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
{/** ... */}
</QueryClientProvider>
</WagmiProvider>
)
}
import { http, createConfig } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
For more information on setting up TanStack Query for Wagmi, follow the Getting Started docs. If you want to set up persistence for your query cache (default behavior before Wagmi v2), check out the TanStack Query docs.
Dropped CommonJS support
Wagmi v2 no longer publishes a separate cjs
tag since very few people use this tag and ESM is the future. See Sindre Sorhus' guide for more info about switching to ESM.
Hooks
Removed mutation setup arguments
Mutation hooks are hooks that change network or application state, sign data, or perform write operations through mutation functions. With Wagmi v1, you could pass arguments directly to these hooks instead of using them with their mutation functions. For example:
// Wagmi v1
const { signMessage } = useSignMessage({
message: 'foo bar baz',
})
With Wagmi v2, you must pass arguments to the mutation function instead. This follows the same behavior as TanStack Query mutations and improves type-safety.
import { useSignMessage } from 'wagmi'
const { signMessage } = useSignMessage({ message: 'foo bar baz' })
const { signMessage } = useSignMessage()
<button
onClick={() => signMessage()}
onClick={() => signMessage({ message: 'foo bar baz' })}
>
Sign message
</button>
Moved TanStack Query parameters to query
property
Previously, you could pass TanStack Query parameters, like enabled
and staleTime
, directly to hooks. In Wagmi v2, TanStack Query parameters are now moved to the query
property. This allows Wagmi to better support TanStack Query type inference, control for future breaking changes since TanStack Query is now a peer dependency, and expose Wagmi-related hook property at the top-level of editor features, like autocomplete.
useReadContract({
enabled: false,
staleTime: 1_000,
query: {
enabled: false,
staleTime: 1_000,
},
})
Removed watch property
The watch
property was removed from all hooks besides useBlock
and useBlockNumber
. This property allowed hooks to internally listen for block changes and automatically refresh their data. In Wagmi v2, you can compose useBlock
or useBlockNumber
along with React.useEffect
to achieve the same behavior. Two different approaches are outlined for useBalance
below.
import { useQueryClient } from '@tanstack/react-query'
import { useEffect } from 'react'
import { useBlockNumber, useBalance } from 'wagmi'
const queryClient = useQueryClient()
const { data: blockNumber } = useBlockNumber({ watch: true })
const { data: balance, queryKey } = useBalance({
address: '0x4557B18E779944BFE9d78A672452331C186a9f48',
watch: true,
})
useEffect(() => {
queryClient.invalidateQueries({ queryKey })
}, [blockNumber, queryClient])
import { useEffect } from 'react'
import { useBlockNumber, useBalance } from 'wagmi'
const { data: blockNumber } = useBlockNumber({ watch: true })
const { data: balance, refetch } = useBalance({
address: '0x4557B18E779944BFE9d78A672452331C186a9f48',
watch: true,
})
useEffect(() => {
refetch()
}, [blockNumber])
This is a bit more code, but removes a lot of internal code from hooks that can slow down your app when not used and gives you more control. For example, you can easily refresh data every five blocks instead of every block.
const { data: blockNumber } = useBlockNumber({ watch: true })
const { data: balance, queryKey } = useBalance({
address: '0x4557B18E779944BFE9d78A672452331C186a9f48',
})
useEffect(() => {
if (blockNumber % 5 === 0)
queryClient.invalidateQueries({ queryKey })
}, [blockNumber, queryClient])
Removed suspense property
Wagmi used to support an experimental suspense
property via TanStack Query. Since TanStack Query removed suspense
from its useQuery
hook, it is no longer supported by Wagmi Hooks.
Instead, you can use useSuspenseQuery
along with TanStack Query-related exports from the 'wagmi/query'
entrypoint.
import { useSuspenseQuery } from '@tanstack/react-query'
import { useConfig } from 'wagmi'
import { getBalanceQueryOptions } from 'wagmi/query'
import { useBalance } from 'wagmi'
const config = useConfig()
const options = getBalanceQueryOptions(config, { address: '0x…' })
const result = useSuspenseQuery(options)
const result = useBalance({
address: '0x…',
suspense: true,
})
Removed prepare hooks
usePrepareContractWrite
and usePrepareSendTransaction
were removed and replaced with idiomatic Viem alternatives. For usePrepareContractWrite
, use useSimulateContract
. Similar to usePrepareContractWrite
, useSimulateContract
composes well with useWriteContract
import { usePrepareContractWrite, useWriteContract } from 'wagmi'
import { useSimulateContract, useWriteContract } from 'wagmi'
const { config } = usePrepareContractWrite({
const { data } = useSimulateContract({
address: '0x',
abi: [{
type: 'function',
name: 'transferFrom',
stateMutability: 'nonpayable',
inputs: [
{ name: 'sender', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [{ type: 'bool' }],
}],
functionName: 'transferFrom',
args: ['0x', '0x', 123n],
})
const { write } = useWriteContract(config)
const { writeContract } = useWriteContract()
<button
disabled={!Boolean(write)}
onClick={() => write()}
disabled={!Boolean(data?.request)}
onClick={() => writeContract(data!.request)}
>
Write contract
</button>
Instead of usePrepareSendTransaction
, use useEstimateGas
. You can pass useEstimateGas
data
to useSendTransaction
to compose the two hooks.
import { usePrepareSendTransaction, useSendTransaction } from 'wagmi'
import { useEstimateGas, useSendTransaction } from 'wagmi'
import { parseEther } from 'viem'
const { config } = usePrepareSendTransaction({
const { data } = useEstimateGas({
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.01'),
})
const { sendTransaction } = useSendTransaction(config)
const { sendTransaction } = useSendTransaction()
<button
disabled={!Boolean(sendTransaction)}
onClick={() => sendTransaction()}
disabled={!Boolean(data)}
onClick={() => sendTransaction({
gas: data,
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.01'),
})}
>
Send transaction
</button>
This might seem like more work, but it gives you more control and is more accurate representation of what is happening under the hood.
Removed useNetwork
hook
The useNetwork
hook was removed since the connected chain is typically based on the connected account. Use useAccount
to get the connected chain
.
import { useNetwork } from 'wagmi'
import { useAccount } from 'wagmi'
const { chain } = useNetwork()
const { chain } = useAccount()
Use useConfig
for the list of chains
set up with the Wagmi Config
.
import { useNetwork } from 'wagmi'
import { useConfig } from 'wagmi'
const { chains } = useNetwork()
const { chains } = useConfig()
Removed onConnect
and onDisconnect
callbacks from useAccount
The onConnect
and onDisconnect
callbacks were removed from the useAccount
hook since it is frequently used without these callbacks so it made sense to extract these into a new API, useAccountEffect
, rather than clutter the useAccount
hook.
import { useAccount } from 'wagmi'
import { useAccountEffect } from 'wagmi'
useAccount({
useAccountEffect({
onConnect(data) {
console.log('connected', data)
},
onDisconnect() {
console.log('disconnected')
},
})
Removed useWebSocketPublicClient
The Wagmi Config
does not separate transport types anymore. Simply use Viem's webSocket
transport instead when setting up your Wagmi Config
. You can get Viem Client
instance with this transport attached by using useClient
or usePublicClient
.
Removed useInfiniteReadContracts
paginatedIndexesConfig
In the spirit of removing unnecessary abstractions, paginatedIndexesConfig
was removed. Use useInfiniteReadContracts
's initialPageParam
and getNextPageParam
parameters along with fetchNextPage
/fetchPreviousPage
from the result instead or copy paginatedIndexesConfig
's implementation to your codebase.
See the TanStack Query docs for more information on infinite queries.
Updated useSendTransaction
and useWriteContract
return type
Updated useSendTransaction
and useWriteContract
return type from { hash: `0x${string}` }
to `0x${string}`
.
const result = useSendTransaction()
result.data?.hash
result.data
Updated useConnect
return type
Updated useConnect
return type from { account: Address; chain: { id: number; unsupported?: boolean }; connector: Connector }
to { accounts: readonly Address[]; chainId: number }
. This better reflects the ability to have multiple accounts per connector.
Renamed parameters and return types
All hook parameters and return types follow the naming pattern of [PascalCaseHookName]Parameters
and [PascalCaseHookName]ReturnType
. For example, UseAccountParameters
and UseAccountReturnType
.
import { UseAccountConfig, UseAccountResult } from 'wagmi'
import { UseAccountParameters, UseAccountReturnType } from 'wagmi'
Connectors
Updated connector API
In order to maximize type-safety and ease of creating connectors, the connector API changed. Follow the Creating Connectors guide for more info on creating new connectors and converting Wagmi v1 connectors.
Removed individual entrypoints
Previously, each connector had it's own entrypoint to optimize tree-shaking. Since all connectors now have package.json#sideEffects
enabled, this is no longer necessary and the entrypoint is unified. Use the 'wagmi/connectors'
entrypoint instead.
import { InjectedConnector } from 'wagmi/connectors/injected'
import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet'
import { coinbaseWallet, injected } from 'wagmi/connectors'
Removed MetaMaskConnector
The MetaMaskConnector
was removed since it was nearly the same thing as the InjectedConnector
. Use the injected
connector instead, along with the target
parameter set to 'metaMask'
, for the same behavior.
import { MetaMaskConnector } from 'wagmi/connectors/metaMask'
import { injected } from 'wagmi/connectors'
const connector = new MetaMaskConnector()
const connector = injected({ target: 'metaMask' })
Renamed connectors
In Wagmi v1, connectors were classes you needed to instantiate. In Wagmi v2, connectors are functions. As a result, the API has changed. Connectors have the following new names:
CoinbaseWalletConnector
is nowcoinbaseWallet
.InjectedConnector
is nowinjected
.SafeConnector
is nowsafe
.WalletConnectConnector
is nowwalletConnect
.
To create a connector, you now call the connector function with parameters.
import { WalletConnectConnector } from 'wagmi/connectors/walletConnect'
import { walletConnect } from 'wagmi/connectors'
const connector = new WalletConnectConnector({
const connector = walletConnect({
projectId: '3fcc6bba6f1de962d911bb5b5c3dba68',
})
Removed WalletConnectLegacyConnector
WalletConnect v1 was sunset June 28, 2023. Use the walletConnect
connector instead.
import { WalletConnectLegacyConnector } from 'wagmi/connectors/walletConnectLegacy'
import { walletConnect } from 'wagmi/connectors'
const connector = new WalletConnectLegacyConnector({
const connector = walletConnect({
projectId: '3fcc6bba6f1de962d911bb5b5c3dba68',
})
Chains
Updated 'wagmi/chains'
entrypoint
Chains now live in the Viem repository. As a result, the 'wagmi/chains'
entrypoint now proxies all chains from 'viem/chains'
directly.
Removed mainnet
and sepolia
from main entrypoint
Since the 'wagmi/chains'
entrypoint now proxies 'viem/chains'
, mainnet
and sepolia
were removed from the main entrypoint. Use the 'wagmi/chains'
entrypoint instead.
import { mainnet, sepolia } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
Errors
A number of errors were renamed to better reflect their functionality or replaced by Viem errors.
Miscellaneous
Removed internal ENS name normalization
Before v2, Wagmi handled ENS name normalization internally for useEnsAddress
, useEnsAvatar
, and useEnsResolver
, using Viem's normalize
function. This added extra bundle size as full normalization is quite heavy. For v2, you must normalize ENS names yourself before passing them to these hooks. You can use Viem's normalize
function or any other function that performs UTS-46 normalization.
import { useEnsAddress } from 'wagmi'
import { normalize } from 'viem/ens'
const result = useEnsAddress({
name: 'wevm.eth',
name: normalize('wevm.eth'),
})
By inverting control, Wagmi let's you choose how much normalization to do. For example, maybe your project only allows ENS names that are rcnumeric so no normalization is not needed. Check out the ENS documentation for more information on normalizing names.
Removed configureChains
The Wagmi v2 Config
now has native multichain support using the chains
parameter so the configureChains
function is no longer required.
import { configureChains, createConfig } from 'wagmi'
import { http, createConfig } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
const { chains, publicClient } = configureChains(
[mainnet, sepolia],
[publicProvider(), publicProvider()],
)
export const config = createConfig({
publicClient,
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
Removed ABI exports
Import from Viem instead.
import { erc20ABI } from 'wagmi'
import { erc20Abi } from 'viem'
Removed 'wagmi/providers/*
entrypoints
It never made sense that we would have provider URLs hardcoded in the Wagmi codebase. Use Viem transports along with RPC provider URLs instead.
import { alchemyProvider } from 'wagmi/providers/alchemy'
import { http } from 'viem'
const transport = http('https://mainnet.example.com')
Updated createConfig
parameters
- Removed
autoConnect
. The reconnecting behavior is now managed by React and not related to the WagmiConfig
. UseWagmiProvider
reconnectOnMount
oruseReconnect
hook instead. - Removed
publicClient
andwebSocketPublicClient
. Usetransports
orclient
instead. - Removed
logger
. Wagmi no longer logs debug information to console.
Updated Config
object
- Removed
config.connector
. Useconfig.state.connections.get(config.state.current)?.connector
instead. - Removed
config.data
. Useconfig.state.connections.get(config.state.current)
instead. - Removed
config.error
. Was unused and not needed. - Removed
config.lastUsedChainId
. Useconfig.state.connections.get(config.state.current)?.chainId
instead. - Removed
config.publicClient
. Useconfig.getClient()
orgetPublicClient
instead. - Removed
config.status
. Useconfig.state.status
instead. - Removed
config.webSocketClient
. Useconfig.getClient()
orgetPublicClient
instead. - Removed
config.clearState
. Was unused and not needed. - Removed
config.autoConnect()
. Usereconnect
action instead. - Renamed
config.setConnectors
. Useconfig._internal.setConnectors
instead. - Removed
config.setLastUsedConnector
. Useconfig.storage?.setItem('recentConnectorId', connectorId)
instead. - Removed
getConfig
.config
should be passed explicitly to actions instead of using globalconfig
.
Deprecations
Renamed WagmiConfig
WagmiConfig
was renamed to WagmiProvider
to reduce confusion with the Wagmi Config
type. React Context Providers usually follow the naming schema *Provider
so this is a more idiomatic name. Now that Wagmi no longer uses Ethers.js (since Wagmi v1), the term "Provider" is less overloaded.
import { WagmiConfig } from 'wagmi'
import { WagmiProvider } from 'wagmi'
import { config } from './config'
function App() {
return (
<WagmiConfig config={config}>
<WagmiProvider config={config}>
{/** ... */}
</WagmiProvider>
</WagmiConfig>
)
}
import { http, createConfig } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
Deprecated useBalance
token
parameter
Moving forward, useBalance
will only work for native currencies, thus the token
parameter is no longer supported. Use useReadContracts
instead.
import { useBalance } from 'wagmi'
import { useReadContracts } from 'wagmi'
import { erc20Abi } from 'viem'
const result = useBalance({
address: '0x4557B18E779944BFE9d78A672452331C186a9f48',
token: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
})
const result = useReadContracts({
allowFailure: false,
contracts: [
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'balanceOf',
args: ['0x4557B18E779944BFE9d78A672452331C186a9f48'],
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'decimals',
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'symbol',
},
]
})
Deprecated useBalance
unit
parameter and formatted
return value
Moving forward, useBalance
will not accept the unit
parameter or return a formatted
value. Instead you can call formatUnits
from Viem directly or use another number formatting library, like dnum instead.
import { formatUnits } from 'viem'
import { useBalance } from 'wagmi'
const result = useBalance({
address: '0x4557B18E779944BFE9d78A672452331C186a9f48',
unit: 'ether',
})
result.data!.formatted
formatUnits(result.data!.value, result.data!.decimals)
Deprecated useToken
Moving forward, useToken
is no longer supported. Use useReadContracts
instead.
import { useToken } from 'wagmi'
import { useReadContracts } from 'wagmi'
import { erc20Abi } from 'viem'
const result = useToken({
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
})
const result = useReadContracts({
allowFailure: false,
contracts: [
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'decimals',
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'name',
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'symbol',
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'totalSupply',
},
]
})
Deprecated formatUnits
parameters and return values
The formatUnits
parameter and related return values (e.g. result.formatted
) are deprecated for the following hooks:
Instead you can call formatUnits
from Viem directly or use another number formatting library, like dnum instead.
import { formatUnits } from 'viem'
const result = useToken({
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
formatUnits: 'ether',
})
result.data!.totalSupply.formatted
formatUnits(result.data!.totalSupply.value, 18)
This allows us to invert control to users so they can handle number formatting however they want, taking into account precision, localization, and more.
Renamed hooks
The following hooks were renamed to better reflect their functionality and underlying Viem actions:
useContractRead
is nowuseReadContract
useContractReads
is nowuseReadContracts
useContractWrite
is nowuseWriteContract
useContractEvent
is nowuseWatchContractEvent
useContractInfiniteReads
is nowuseInfiniteReadContracts
useFeeData
is nowuseEstimateFeesPerGas
useSwitchNetwork
is nowuseSwitchChain
useWaitForTransaction
is nowuseWaitForTransactionReceipt
Miscellaneous
WagmiConfigProps
renamed toWagmiProviderProps
.Context
renamed toWagmiContext
.