0
# TanStack Query Integration
1
2
First-class TanStack Query support with query options, mutation options, and caching strategies.
3
4
## Overview
5
6
@wagmi/core provides comprehensive TanStack Query integration through the `/query` export. This integration offers optimized caching, background refetching, and reactive data management for all blockchain operations.
7
8
## Core Query Functions
9
10
### Query Options for Read Operations
11
12
Generate TanStack Query options for read operations that automatically handle caching and refetching.
13
14
```typescript { .api }
15
/**
16
* Query options for balance queries
17
* @param config - Wagmi configuration
18
* @param options - Balance query options
19
* @returns TanStack Query options
20
*/
21
function getBalanceQueryOptions<config extends Config>(
22
config: config,
23
options: GetBalanceOptions<config>
24
): QueryOptions<GetBalanceData, GetBalanceErrorType>;
25
26
interface GetBalanceOptions<config extends Config> {
27
/** Account address */
28
address: Address;
29
/** Token contract address (optional) */
30
token?: Address;
31
/** Chain ID */
32
chainId?: config['chains'][number]['id'];
33
/** Query configuration */
34
query?: {
35
enabled?: boolean;
36
staleTime?: number;
37
gcTime?: number;
38
};
39
}
40
41
type GetBalanceData = {
42
decimals: number;
43
formatted: string;
44
symbol: string;
45
value: bigint;
46
};
47
48
/**
49
* Query key for balance queries
50
* @param parameters - Balance parameters
51
* @returns Query key array
52
*/
53
function getBalanceQueryKey(parameters?: GetBalanceParameters): GetBalanceQueryKey;
54
55
type GetBalanceQueryKey = readonly ['balance', GetBalanceParameters];
56
57
type GetBalanceQueryFnData = GetBalanceReturnType;
58
```
59
60
**Usage Example:**
61
62
```typescript
63
import { useQuery } from '@tanstack/react-query'
64
import { getBalanceQueryOptions } from '@wagmi/core/query'
65
66
// In React component
67
function BalanceDisplay({ address }: { address: Address }) {
68
const { data: balance, isLoading, error } = useQuery(
69
getBalanceQueryOptions(config, {
70
address,
71
query: {
72
staleTime: 1000 * 60, // 1 minute
73
enabled: !!address,
74
},
75
})
76
)
77
78
if (isLoading) return <div>Loading balance...</div>
79
if (error) return <div>Error loading balance</div>
80
if (!balance) return null
81
82
return (
83
<div>
84
Balance: {balance.formatted} {balance.symbol}
85
</div>
86
)
87
}
88
89
// In vanilla JavaScript
90
import { QueryClient } from '@tanstack/query-core'
91
92
const queryClient = new QueryClient()
93
94
const balance = await queryClient.fetchQuery(
95
getBalanceQueryOptions(config, {
96
address: '0x742d35Cc6601C2F3Ac5e5c7A9d16e4e6Be4e6e9e',
97
})
98
)
99
console.log('Balance:', balance.formatted)
100
```
101
102
### Contract Read Query Options
103
104
Query options for contract read operations.
105
106
```typescript { .api }
107
/**
108
* Query options for contract reads
109
* @param config - Wagmi configuration
110
* @param options - Contract read options
111
* @returns TanStack Query options
112
*/
113
function readContractQueryOptions<config extends Config>(
114
config: config,
115
options: ReadContractOptions<config>
116
): QueryOptions<ReadContractData, ReadContractErrorType>;
117
118
interface ReadContractOptions<config extends Config> {
119
/** Contract address */
120
address: Address;
121
/** Contract ABI */
122
abi: Abi;
123
/** Function name */
124
functionName: string;
125
/** Function arguments */
126
args?: readonly unknown[];
127
/** Chain ID */
128
chainId?: config['chains'][number]['id'];
129
/** Query configuration */
130
query?: {
131
enabled?: boolean;
132
staleTime?: number;
133
gcTime?: number;
134
};
135
}
136
137
type ReadContractData = any; // Depends on contract function
138
139
/**
140
* Query key for contract reads
141
* @param parameters - Contract read parameters
142
* @returns Query key array
143
*/
144
function readContractQueryKey(parameters?: ReadContractParameters): ReadContractQueryKey;
145
146
type ReadContractQueryKey = readonly ['readContract', ReadContractParameters];
147
148
/**
149
* Query options for multiple contract reads
150
* @param config - Wagmi configuration
151
* @param options - Multiple contract read options
152
* @returns TanStack Query options
153
*/
154
function readContractsQueryOptions<config extends Config>(
155
config: config,
156
options: ReadContractsOptions<config>
157
): QueryOptions<ReadContractsData, ReadContractsErrorType>;
158
159
interface ReadContractsOptions<config extends Config> {
160
/** Array of contract calls */
161
contracts: readonly {
162
address: Address;
163
abi: Abi;
164
functionName: string;
165
args?: readonly unknown[];
166
}[];
167
/** Chain ID */
168
chainId?: config['chains'][number]['id'];
169
/** Allow failures */
170
allowFailure?: boolean;
171
/** Query configuration */
172
query?: {
173
enabled?: boolean;
174
staleTime?: number;
175
gcTime?: number;
176
};
177
}
178
179
type ReadContractsData = ReadContractsReturnType;
180
```
181
182
**Usage Example:**
183
184
```typescript
185
import { useQuery } from '@tanstack/react-query'
186
import { readContractQueryOptions } from '@wagmi/core/query'
187
188
// Single contract read
189
function TokenName({ address }: { address: Address }) {
190
const { data: name } = useQuery(
191
readContractQueryOptions(config, {
192
address,
193
abi: erc20Abi,
194
functionName: 'name',
195
query: {
196
staleTime: 1000 * 60 * 60, // 1 hour (name rarely changes)
197
},
198
})
199
)
200
201
return <div>Token: {name}</div>
202
}
203
204
// Multiple contract reads
205
function TokenInfo({ address }: { address: Address }) {
206
const { data: tokenData } = useQuery(
207
readContractsQueryOptions(config, {
208
contracts: [
209
{ address, abi: erc20Abi, functionName: 'name' },
210
{ address, abi: erc20Abi, functionName: 'symbol' },
211
{ address, abi: erc20Abi, functionName: 'decimals' },
212
{ address, abi: erc20Abi, functionName: 'totalSupply' },
213
],
214
})
215
)
216
217
if (!tokenData) return null
218
219
return (
220
<div>
221
<div>Name: {tokenData[0].result}</div>
222
<div>Symbol: {tokenData[1].result}</div>
223
<div>Decimals: {tokenData[2].result}</div>
224
<div>Total Supply: {tokenData[3].result?.toString()}</div>
225
</div>
226
)
227
}
228
```
229
230
### Blockchain Data Query Options
231
232
Query options for blockchain data like blocks, transactions, and network information.
233
234
```typescript { .api }
235
/**
236
* Query options for block data
237
* @param config - Wagmi configuration
238
* @param options - Block query options
239
* @returns TanStack Query options
240
*/
241
function getBlockQueryOptions<config extends Config>(
242
config: config,
243
options: GetBlockOptions<config>
244
): QueryOptions<GetBlockData, GetBlockErrorType>;
245
246
interface GetBlockOptions<config extends Config> {
247
/** Block number */
248
blockNumber?: bigint;
249
/** Block hash */
250
blockHash?: Hash;
251
/** Block tag */
252
blockTag?: 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized';
253
/** Include transactions */
254
includeTransactions?: boolean;
255
/** Chain ID */
256
chainId?: config['chains'][number]['id'];
257
/** Query configuration */
258
query?: {
259
enabled?: boolean;
260
staleTime?: number;
261
gcTime?: number;
262
};
263
}
264
265
type GetBlockData = GetBlockReturnType;
266
267
/**
268
* Query options for block number
269
* @param config - Wagmi configuration
270
* @param options - Block number options
271
* @returns TanStack Query options
272
*/
273
function getBlockNumberQueryOptions<config extends Config>(
274
config: config,
275
options: GetBlockNumberOptions<config>
276
): QueryOptions<GetBlockNumberData, GetBlockNumberErrorType>;
277
278
interface GetBlockNumberOptions<config extends Config> {
279
/** Chain ID */
280
chainId?: config['chains'][number]['id'];
281
/** Query configuration */
282
query?: {
283
enabled?: boolean;
284
staleTime?: number;
285
gcTime?: number;
286
refetchInterval?: number; // Auto-refetch for latest block
287
};
288
}
289
290
type GetBlockNumberData = bigint;
291
292
/**
293
* Query options for transaction data
294
* @param config - Wagmi configuration
295
* @param options - Transaction query options
296
* @returns TanStack Query options
297
*/
298
function getTransactionQueryOptions<config extends Config>(
299
config: config,
300
options: GetTransactionOptions<config>
301
): QueryOptions<GetTransactionData, GetTransactionErrorType>;
302
303
interface GetTransactionOptions<config extends Config> {
304
/** Transaction hash */
305
hash: Hash;
306
/** Chain ID */
307
chainId?: config['chains'][number]['id'];
308
/** Query configuration */
309
query?: {
310
enabled?: boolean;
311
staleTime?: number;
312
gcTime?: number;
313
};
314
}
315
316
type GetTransactionData = GetTransactionReturnType;
317
318
/**
319
* Query options for transaction receipt
320
* @param config - Wagmi configuration
321
* @param options - Transaction receipt options
322
* @returns TanStack Query options
323
*/
324
function getTransactionReceiptQueryOptions<config extends Config>(
325
config: config,
326
options: GetTransactionReceiptOptions<config>
327
): QueryOptions<GetTransactionReceiptData, GetTransactionReceiptErrorType>;
328
329
interface GetTransactionReceiptOptions<config extends Config> {
330
/** Transaction hash */
331
hash: Hash;
332
/** Chain ID */
333
chainId?: config['chains'][number]['id'];
334
/** Query configuration */
335
query?: {
336
enabled?: boolean;
337
staleTime?: number;
338
gcTime?: number;
339
};
340
}
341
342
type GetTransactionReceiptData = GetTransactionReceiptReturnType;
343
```
344
345
### Additional Query Options
346
347
Additional blockchain operations and client query options.
348
349
```typescript { .api }
350
/**
351
* Query options for arbitrary contract calls
352
* @param config - Wagmi configuration
353
* @param options - Call options
354
* @returns TanStack Query options
355
*/
356
function callQueryOptions<config extends Config>(
357
config: config,
358
options: CallOptions<config>
359
): QueryOptions<CallData, CallErrorType>;
360
361
/**
362
* Query options for Merkle proofs
363
* @param config - Wagmi configuration
364
* @param options - Proof options
365
* @returns TanStack Query options
366
*/
367
function getProofQueryOptions<config extends Config>(
368
config: config,
369
options: GetProofOptions<config>
370
): QueryOptions<GetProofData, GetProofErrorType>;
371
372
/**
373
* Query options for connector clients
374
* @param config - Wagmi configuration
375
* @param options - Connector client options
376
* @returns TanStack Query options
377
*/
378
function getConnectorClientQueryOptions<config extends Config>(
379
config: config,
380
options: GetConnectorClientOptions<config>
381
): QueryOptions<GetConnectorClientData, GetConnectorClientErrorType>;
382
383
/**
384
* Query options for wallet clients
385
* @param config - Wagmi configuration
386
* @param options - Wallet client options
387
* @returns TanStack Query options
388
*/
389
function getWalletClientQueryOptions<config extends Config>(
390
config: config,
391
options: GetWalletClientOptions<config>
392
): QueryOptions<GetWalletClientData, GetWalletClientErrorType>;
393
```
394
395
## Mutation Options for Write Operations
396
397
Generate TanStack Query mutation options for write operations like transactions and contract interactions.
398
399
```typescript { .api }
400
/**
401
* Mutation options for sending transactions
402
* @param config - Wagmi configuration
403
* @returns TanStack Query mutation options
404
*/
405
function sendTransactionMutationOptions<config extends Config>(
406
config: config
407
): MutationOptions<SendTransactionData, SendTransactionErrorType, SendTransactionVariables>;
408
409
interface SendTransactionVariables {
410
/** Target address */
411
to?: Address;
412
/** Transaction data */
413
data?: Hex;
414
/** Gas limit */
415
gas?: bigint;
416
/** Gas price */
417
gasPrice?: bigint;
418
/** Max fee per gas */
419
maxFeePerGas?: bigint;
420
/** Max priority fee per gas */
421
maxPriorityFeePerGas?: bigint;
422
/** Nonce */
423
nonce?: number;
424
/** Value to send */
425
value?: bigint;
426
/** Chain ID */
427
chainId?: number;
428
}
429
430
type SendTransactionData = Hash;
431
432
type SendTransactionMutate = (variables: SendTransactionVariables) => void;
433
type SendTransactionMutateAsync = (variables: SendTransactionVariables) => Promise<SendTransactionData>;
434
435
/**
436
* Mutation options for contract writes
437
* @param config - Wagmi configuration
438
* @returns TanStack Query mutation options
439
*/
440
function writeContractMutationOptions<config extends Config>(
441
config: config
442
): MutationOptions<WriteContractData, WriteContractErrorType, WriteContractVariables>;
443
444
interface WriteContractVariables {
445
/** Contract address */
446
address: Address;
447
/** Contract ABI */
448
abi: Abi;
449
/** Function name */
450
functionName: string;
451
/** Function arguments */
452
args?: readonly unknown[];
453
/** Chain ID */
454
chainId?: number;
455
/** Gas options */
456
gas?: bigint;
457
gasPrice?: bigint;
458
maxFeePerGas?: bigint;
459
maxPriorityFeePerGas?: bigint;
460
/** Transaction value */
461
value?: bigint;
462
}
463
464
type WriteContractData = Hash;
465
466
type WriteContractMutate = (variables: WriteContractVariables) => void;
467
type WriteContractMutateAsync = (variables: WriteContractVariables) => Promise<WriteContractData>;
468
469
/**
470
* Mutation options for wallet connection
471
* @param config - Wagmi configuration
472
* @returns TanStack Query mutation options
473
*/
474
function connectMutationOptions<config extends Config>(
475
config: config
476
): MutationOptions<ConnectData, ConnectErrorType, ConnectVariables>;
477
478
interface ConnectVariables {
479
/** Connector to connect with */
480
connector: Connector | CreateConnectorFn;
481
/** Chain ID to connect to */
482
chainId?: number;
483
}
484
485
type ConnectData = {
486
accounts: readonly [Address, ...Address[]];
487
chainId: number;
488
};
489
490
type ConnectMutate = (variables: ConnectVariables) => void;
491
type ConnectMutateAsync = (variables: ConnectVariables) => Promise<ConnectData>;
492
```
493
494
**Usage Example:**
495
496
```typescript
497
import { useMutation, useQueryClient } from '@tanstack/react-query'
498
import { sendTransactionMutationOptions, getBalanceQueryKey } from '@wagmi/core/query'
499
500
function SendTransactionButton() {
501
const queryClient = useQueryClient()
502
503
const {
504
mutate: sendTransaction,
505
mutateAsync: sendTransactionAsync,
506
isPending,
507
error,
508
} = useMutation({
509
...sendTransactionMutationOptions(config),
510
onSuccess: (hash) => {
511
console.log('Transaction sent:', hash)
512
// Invalidate balance queries to refetch
513
queryClient.invalidateQueries({
514
queryKey: getBalanceQueryKey(),
515
})
516
},
517
})
518
519
const handleSend = () => {
520
sendTransaction({
521
to: '0x742d35Cc6601C2F3Ac5e5c7A9d16e4e6Be4e6e9e',
522
value: parseEther('0.1'),
523
})
524
}
525
526
const handleSendAsync = async () => {
527
try {
528
const hash = await sendTransactionAsync({
529
to: '0x742d35Cc6601C2F3Ac5e5c7A9d16e4e6Be4e6e9e',
530
value: parseEther('0.1'),
531
})
532
console.log('Transaction sent:', hash)
533
} catch (error) {
534
console.error('Transaction failed:', error)
535
}
536
}
537
538
return (
539
<div>
540
<button onClick={handleSend} disabled={isPending}>
541
{isPending ? 'Sending...' : 'Send Transaction'}
542
</button>
543
<button onClick={handleSendAsync} disabled={isPending}>
544
Send Async
545
</button>
546
{error && <div>Error: {error.message}</div>}
547
</div>
548
)
549
}
550
```
551
552
## Advanced Query Options
553
554
### Infinite Queries
555
556
For paginated data like contract events or transaction lists.
557
558
```typescript { .api }
559
/**
560
* Infinite query options for contract reads
561
* @param config - Wagmi configuration
562
* @param options - Infinite query options
563
* @returns TanStack Query infinite options
564
*/
565
function infiniteReadContractsQueryOptions<config extends Config>(
566
config: config,
567
options: InfiniteReadContractsOptions<config>
568
): InfiniteQueryOptions<InfiniteReadContractsData, InfiniteReadContractsErrorType>;
569
570
interface InfiniteReadContractsOptions<config extends Config> {
571
/** Array of contract calls with pagination */
572
contracts: (pageParam: unknown) => readonly {
573
address: Address;
574
abi: Abi;
575
functionName: string;
576
args?: readonly unknown[];
577
}[];
578
/** Chain ID */
579
chainId?: config['chains'][number]['id'];
580
/** Query configuration */
581
query?: {
582
enabled?: boolean;
583
staleTime?: number;
584
gcTime?: number;
585
getNextPageParam?: (lastPage: any, allPages: any[]) => unknown;
586
getPreviousPageParam?: (firstPage: any, allPages: any[]) => unknown;
587
};
588
}
589
590
type InfiniteReadContractsData = {
591
pages: ReadContractsReturnType[];
592
pageParams: unknown[];
593
};
594
595
type InfiniteReadContractsQueryKey = readonly ['infiniteReadContracts', InfiniteReadContractsOptions];
596
597
type InfiniteReadContractsQueryFnData = ReadContractsReturnType;
598
```
599
600
### Query Utilities
601
602
Utility functions for working with query keys and caching.
603
604
```typescript { .api }
605
/**
606
* Hash function for query keys
607
* @param queryKey - Query key to hash
608
* @returns Hash string
609
*/
610
function hashFn(queryKey: QueryKey): string;
611
612
/**
613
* Structural sharing for query data
614
* @param oldData - Previous data
615
* @param newData - New data
616
* @returns Optimized data with structural sharing
617
*/
618
function structuralSharing<T>(oldData: T | undefined, newData: T): T;
619
620
type QueryKey = readonly unknown[];
621
```
622
623
**Usage Example:**
624
625
```typescript
626
import { hashFn, structuralSharing } from '@wagmi/core/query'
627
628
// Custom query with optimized caching
629
const customQueryOptions = {
630
queryKey: ['custom', 'data'],
631
queryFn: async () => {
632
// Fetch data
633
return await fetchCustomData()
634
},
635
select: (data: any) => structuralSharing(undefined, data),
636
queryKeyHashFn: hashFn,
637
}
638
639
// Use with QueryClient
640
const queryClient = new QueryClient({
641
defaultOptions: {
642
queries: {
643
queryKeyHashFn: hashFn,
644
structuralSharing,
645
},
646
},
647
})
648
```
649
650
## Complete Integration Example
651
652
Here's a comprehensive example showing how to use TanStack Query with wagmi:
653
654
```typescript
655
import { QueryClient, useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
656
import {
657
getBalanceQueryOptions,
658
readContractQueryOptions,
659
writeContractMutationOptions,
660
getBalanceQueryKey,
661
} from '@wagmi/core/query'
662
663
// Set up QueryClient with wagmi optimizations
664
const queryClient = new QueryClient({
665
defaultOptions: {
666
queries: {
667
staleTime: 1000 * 60, // 1 minute
668
gcTime: 1000 * 60 * 60 * 24, // 24 hours
669
},
670
},
671
})
672
673
// React component example
674
function TokenManager({ tokenAddress, userAddress }: {
675
tokenAddress: Address
676
userAddress: Address
677
}) {
678
const queryClient = useQueryClient()
679
680
// Query user's token balance
681
const { data: balance, isLoading: balanceLoading } = useQuery(
682
getBalanceQueryOptions(config, {
683
address: userAddress,
684
token: tokenAddress,
685
query: {
686
enabled: !!userAddress && !!tokenAddress,
687
staleTime: 1000 * 30, // 30 seconds for balance
688
},
689
})
690
)
691
692
// Query token metadata
693
const { data: tokenName } = useQuery(
694
readContractQueryOptions(config, {
695
address: tokenAddress,
696
abi: erc20Abi,
697
functionName: 'name',
698
query: {
699
enabled: !!tokenAddress,
700
staleTime: 1000 * 60 * 60, // 1 hour for token name
701
},
702
})
703
)
704
705
// Mutation for token transfer
706
const {
707
mutate: transfer,
708
isPending: transferPending,
709
error: transferError,
710
} = useMutation({
711
...writeContractMutationOptions(config),
712
onSuccess: (hash) => {
713
console.log('Transfer successful:', hash)
714
715
// Invalidate and refetch balance
716
queryClient.invalidateQueries({
717
queryKey: getBalanceQueryKey({ address: userAddress, token: tokenAddress }),
718
})
719
720
// Optimistically update balance (optional)
721
queryClient.setQueryData(
722
getBalanceQueryKey({ address: userAddress, token: tokenAddress }),
723
(oldBalance: any) => {
724
if (!oldBalance) return oldBalance
725
return {
726
...oldBalance,
727
value: oldBalance.value - transferAmount, // Subtract transferred amount
728
formatted: formatUnits(oldBalance.value - transferAmount, oldBalance.decimals),
729
}
730
}
731
)
732
},
733
onError: (error) => {
734
console.error('Transfer failed:', error)
735
},
736
})
737
738
const handleTransfer = (to: Address, amount: bigint) => {
739
transfer({
740
address: tokenAddress,
741
abi: erc20Abi,
742
functionName: 'transfer',
743
args: [to, amount],
744
})
745
}
746
747
if (balanceLoading) return <div>Loading...</div>
748
749
return (
750
<div>
751
<h3>{tokenName}</h3>
752
<p>Balance: {balance?.formatted} {balance?.symbol}</p>
753
754
<button
755
onClick={() => handleTransfer('0xRecipient', parseUnits('10', balance?.decimals || 18))}
756
disabled={transferPending}
757
>
758
{transferPending ? 'Transferring...' : 'Transfer 10 tokens'}
759
</button>
760
761
{transferError && (
762
<div>Error: {transferError.message}</div>
763
)}
764
</div>
765
)
766
}
767
768
// App setup
769
function App() {
770
return (
771
<QueryClientProvider client={queryClient}>
772
<TokenManager
773
tokenAddress="0xA0b86a33E6411c0B7f8C4b5d3e1B9d3e8b4a4e6f"
774
userAddress="0x742d35Cc6601C2F3Ac5e5c7A9d16e4e6Be4e6e9e"
775
/>
776
</QueryClientProvider>
777
)
778
}
779
```
780
781
This integration provides:
782
- Automatic caching and background refetching
783
- Optimistic updates for better UX
784
- Query invalidation after mutations
785
- Type safety with wagmi's typed functions
786
- Efficient data fetching and synchronization