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-querynpm install wagmi viem@2.x @tanstack/react-queryyarn add wagmi viem@2.x @tanstack/react-querybun add wagmi viem@2.x @tanstack/react-queryWagmi 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.dataUpdated 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:
CoinbaseWalletConnectoris nowcoinbaseWallet.InjectedConnectoris nowinjected.SafeConnectoris nowsafe.WalletConnectConnectoris 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 numeric 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. UseWagmiProviderreconnectOnMountoruseReconnecthook instead. - Removed
publicClientandwebSocketPublicClient. Usetransportsorclientinstead. - Removed
logger. Wagmi no longer logs debug information to console.
Updated Config object
- Removed
config.connector. Useconfig.state.connections.get(config.state.current)?.connectorinstead. - 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)?.chainIdinstead. - Removed
config.publicClient. Useconfig.getClient()orgetPublicClientinstead. - Removed
config.status. Useconfig.state.statusinstead. - Removed
config.webSocketClient. Useconfig.getClient()orgetPublicClientinstead. - Removed
config.clearState. Was unused and not needed. - Removed
config.autoConnect(). Usereconnectaction instead. - Renamed
config.setConnectors. Useconfig._internal.setConnectorsinstead. - Removed
config.setLastUsedConnector. Useconfig.storage?.setItem('recentConnectorId', connectorId)instead. - Removed
getConfig.configshould 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:
useContractReadis nowuseReadContractuseContractReadsis nowuseReadContractsuseContractWriteis nowuseWriteContractuseContractEventis nowuseWatchContractEventuseContractInfiniteReadsis nowuseInfiniteReadContractsuseFeeDatais nowuseEstimateFeesPerGasuseSwitchNetworkis nowuseSwitchChainuseWaitForTransactionis nowuseWaitForTransactionReceipt
Miscellaneous
WagmiConfigPropsrenamed toWagmiProviderProps.Contextrenamed toWagmiContext.