ESLint plugin for TanStack Query that provides comprehensive linting rules to enforce best practices and prevent common pitfalls.
npx @tessl/cli install tessl/npm-tanstack--eslint-plugin-query@5.86.00
# @tanstack/eslint-plugin-query
1
2
@tanstack/eslint-plugin-query is an ESLint plugin specifically designed for TanStack Query applications. It provides a comprehensive set of linting rules to enforce best practices and prevent common pitfalls when using TanStack Query, helping developers avoid runtime errors and maintain consistent coding patterns.
3
4
## Package Information
5
6
- **Package Name**: @tanstack/eslint-plugin-query
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @tanstack/eslint-plugin-query`
10
11
## Core Imports
12
13
```typescript
14
import queryPlugin from "@tanstack/eslint-plugin-query";
15
// Named exports also available:
16
import { Plugin } from "@tanstack/eslint-plugin-query";
17
```
18
19
For legacy configuration:
20
21
```javascript
22
const queryPlugin = require("@tanstack/eslint-plugin-query");
23
```
24
25
## Basic Usage
26
27
### Modern Flat Config (ESLint 9+)
28
29
```javascript
30
import queryPlugin from "@tanstack/eslint-plugin-query";
31
32
export default [
33
// Other configs...
34
...queryPlugin.configs["flat/recommended"]
35
];
36
```
37
38
### Legacy Config (ESLint 8)
39
40
```javascript
41
module.exports = {
42
plugins: ["@tanstack/query"],
43
extends: ["plugin:@tanstack/query/recommended"],
44
// Or configure individual rules:
45
rules: {
46
"@tanstack/query/exhaustive-deps": "error",
47
"@tanstack/query/no-rest-destructuring": "warn",
48
"@tanstack/query/stable-query-client": "error",
49
}
50
};
51
```
52
53
## Capabilities
54
55
### Plugin Structure
56
57
Main plugin object with rules and configurations.
58
59
```typescript { .api }
60
interface Plugin extends Omit<ESLint.Plugin, 'rules'> {
61
rules: Record<RuleKey, RuleModule<any, any, any>>;
62
configs: {
63
recommended: ESLint.ConfigData;
64
'flat/recommended': Array<Linter.Config>;
65
};
66
}
67
68
const plugin: Plugin;
69
export default plugin;
70
```
71
72
### Configuration Presets
73
74
Pre-configured rule sets for immediate use.
75
76
```typescript { .api }
77
// Legacy configuration format
78
interface RecommendedConfig {
79
plugins: string[];
80
rules: {
81
'@tanstack/query/exhaustive-deps': 'error';
82
'@tanstack/query/no-rest-destructuring': 'warn';
83
'@tanstack/query/stable-query-client': 'error';
84
'@tanstack/query/no-unstable-deps': 'error';
85
'@tanstack/query/infinite-query-property-order': 'error';
86
'@tanstack/query/no-void-query-fn': 'error';
87
'@tanstack/query/mutation-property-order': 'error';
88
};
89
}
90
91
// Modern flat configuration format
92
interface FlatRecommendedConfig extends Array<{
93
name: string;
94
plugins: Record<string, Plugin>;
95
rules: Record<string, string>;
96
}> {}
97
```
98
99
### ESLint Rules
100
101
Collection of seven specialized linting rules for TanStack Query applications.
102
103
#### Exhaustive Dependencies Rule
104
105
Ensures all dependencies used in queryFn are included in queryKey to prevent stale closure issues.
106
107
```typescript { .api }
108
// Rule: @tanstack/query/exhaustive-deps
109
// Severity: error
110
// Description: Exhaustive deps rule for useQuery
111
// Provides auto-fix suggestions for missing dependencies
112
```
113
114
**Usage Example:**
115
```typescript
116
// ❌ Bad - missing dependencies in queryKey
117
useQuery({
118
queryKey: ['user'],
119
queryFn: () => fetchUser(userId, settings), // userId and settings not in queryKey
120
});
121
122
// ✅ Good - all dependencies included
123
useQuery({
124
queryKey: ['user', userId, settings],
125
queryFn: () => fetchUser(userId, settings),
126
});
127
```
128
129
#### No Rest Destructuring Rule
130
131
Disallows rest destructuring in queries to prevent excessive re-renders.
132
133
```typescript { .api }
134
// Rule: @tanstack/query/no-rest-destructuring
135
// Severity: warn
136
// Description: Disallows rest destructuring in queries
137
```
138
139
**Usage Example:**
140
```typescript
141
// ❌ Bad - rest destructuring can cause issues
142
const { data, ...rest } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
143
144
// ✅ Good - explicit destructuring
145
const { data, isLoading, error } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
146
```
147
148
#### Stable Query Client Rule
149
150
Makes sure that QueryClient is stable to prevent unnecessary re-renders.
151
152
```typescript { .api }
153
// Rule: @tanstack/query/stable-query-client
154
// Severity: error
155
// Description: Makes sure that QueryClient is stable
156
```
157
158
**Usage Example:**
159
```typescript
160
// ❌ Bad - creating new QueryClient instance on each render
161
function App() {
162
return (
163
<QueryClientProvider client={new QueryClient()}>
164
<MyComponent />
165
</QueryClientProvider>
166
);
167
}
168
169
// ✅ Good - stable QueryClient instance
170
const queryClient = new QueryClient();
171
function App() {
172
return (
173
<QueryClientProvider client={queryClient}>
174
<MyComponent />
175
</QueryClientProvider>
176
);
177
}
178
```
179
180
#### No Unstable Dependencies Rule
181
182
Disallows putting the result of query hooks directly in a React hook dependency array to prevent infinite re-render loops.
183
184
```typescript { .api }
185
// Rule: @tanstack/query/no-unstable-deps
186
// Severity: error
187
// Description: Disallow putting the result of query hooks directly in a React hook dependency array
188
```
189
190
**Usage Example:**
191
```typescript
192
// ❌ Bad - using query result directly in dependency array
193
const { data: user } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
194
useEffect(() => {
195
console.log('User changed');
196
}, [user]); // This causes infinite re-renders
197
198
// ✅ Good - use specific properties or data content
199
useEffect(() => {
200
console.log('User changed');
201
}, [user?.id]); // Only re-run when user ID changes
202
```
203
204
#### Infinite Query Property Order Rule
205
206
Ensures correct order of inference sensitive properties for infinite queries.
207
208
```typescript { .api }
209
// Rule: @tanstack/query/infinite-query-property-order
210
// Severity: error
211
// Description: Ensure correct order of inference sensitive properties for infinite queries
212
```
213
214
**Usage Example:**
215
```typescript
216
// ✅ Good - properties in correct order
217
useInfiniteQuery({
218
queryKey: ['posts'],
219
queryFn: fetchPosts,
220
initialPageParam: 0,
221
getNextPageParam: (lastPage) => lastPage.nextCursor,
222
});
223
```
224
225
#### No Void Query Function Rule
226
227
Ensures queryFn returns a non-undefined value to prevent runtime issues.
228
229
```typescript { .api }
230
// Rule: @tanstack/query/no-void-query-fn
231
// Severity: error
232
// Description: Ensures queryFn returns a non-undefined value
233
```
234
235
**Usage Example:**
236
```typescript
237
// ❌ Bad - query function returns void
238
useQuery({
239
queryKey: ['user'],
240
queryFn: () => { console.log('fetching'); }, // returns void
241
});
242
243
// ✅ Good - query function returns data
244
useQuery({
245
queryKey: ['user'],
246
queryFn: () => fetchUser(), // returns Promise<User>
247
});
248
```
249
250
#### Mutation Property Order Rule
251
252
Ensures correct order of inference-sensitive properties in useMutation().
253
254
```typescript { .api }
255
// Rule: @tanstack/query/mutation-property-order
256
// Severity: error
257
// Description: Ensure correct order of inference-sensitive properties in useMutation()
258
```
259
260
**Usage Example:**
261
```typescript
262
// ✅ Good - properties in correct order
263
useMutation({
264
mutationFn: updateUser,
265
onSuccess: (data) => {
266
queryClient.invalidateQueries({ queryKey: ['user'] });
267
},
268
onError: (error) => {
269
console.error('Update failed:', error);
270
},
271
});
272
```
273
274
## Types
275
276
```typescript { .api }
277
import type { ESLint, Linter } from 'eslint';
278
import type { RuleModule } from '@typescript-eslint/utils/ts-eslint';
279
280
type RuleKey = keyof typeof rules;
281
282
interface Plugin extends Omit<ESLint.Plugin, 'rules'> {
283
rules: Record<RuleKey, RuleModule<any, any, any>>;
284
configs: {
285
recommended: ESLint.ConfigData;
286
'flat/recommended': Array<Linter.Config>;
287
};
288
}
289
290
type ExtraRuleDocs = {
291
recommended: 'strict' | 'error' | 'warn';
292
}
293
```
294
295
## Framework Compatibility
296
297
This ESLint plugin works with all TanStack Query framework adapters and automatically detects the correct query hooks from:
298
299
- **React**: `@tanstack/react-query`
300
- **Vue**: `@tanstack/vue-query`
301
- **Solid**: `@tanstack/solid-query`
302
- **Svelte**: `@tanstack/svelte-query`
303
- **Angular**: `@tanstack/angular-query-experimental`
304
305
The plugin uses intelligent import detection and only applies rules to code that uses TanStack Query hooks.
306
307
## Error Handling
308
309
The plugin automatically detects TanStack Query imports and only applies rules to relevant code. Rules provide detailed error messages and auto-fix suggestions where possible. If you encounter false positives, ensure your TanStack Query imports follow standard patterns:
310
311
```typescript
312
// Supported import patterns
313
import { useQuery } from '@tanstack/react-query';
314
import { useQuery } from '@tanstack/vue-query';
315
import { useQuery } from '@tanstack/solid-query';
316
import { useQuery } from '@tanstack/svelte-query';
317
```
318
319
## TypeScript Integration
320
321
The plugin includes full TypeScript definitions and integrates seamlessly with TypeScript ESLint parser. When using TypeScript, the plugin can provide enhanced type-aware linting for improved accuracy.