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.0@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.
npm install @tanstack/eslint-plugin-queryimport queryPlugin from "@tanstack/eslint-plugin-query";
// Named exports also available:
import { Plugin } from "@tanstack/eslint-plugin-query";For legacy configuration:
const queryPlugin = require("@tanstack/eslint-plugin-query");import queryPlugin from "@tanstack/eslint-plugin-query";
export default [
// Other configs...
...queryPlugin.configs["flat/recommended"]
];module.exports = {
plugins: ["@tanstack/query"],
extends: ["plugin:@tanstack/query/recommended"],
// Or configure individual rules:
rules: {
"@tanstack/query/exhaustive-deps": "error",
"@tanstack/query/no-rest-destructuring": "warn",
"@tanstack/query/stable-query-client": "error",
}
};Main plugin object with rules and configurations.
interface Plugin extends Omit<ESLint.Plugin, 'rules'> {
rules: Record<RuleKey, RuleModule<any, any, any>>;
configs: {
recommended: ESLint.ConfigData;
'flat/recommended': Array<Linter.Config>;
};
}
const plugin: Plugin;
export default plugin;Pre-configured rule sets for immediate use.
// Legacy configuration format
interface RecommendedConfig {
plugins: string[];
rules: {
'@tanstack/query/exhaustive-deps': 'error';
'@tanstack/query/no-rest-destructuring': 'warn';
'@tanstack/query/stable-query-client': 'error';
'@tanstack/query/no-unstable-deps': 'error';
'@tanstack/query/infinite-query-property-order': 'error';
'@tanstack/query/no-void-query-fn': 'error';
'@tanstack/query/mutation-property-order': 'error';
};
}
// Modern flat configuration format
interface FlatRecommendedConfig extends Array<{
name: string;
plugins: Record<string, Plugin>;
rules: Record<string, string>;
}> {}Collection of seven specialized linting rules for TanStack Query applications.
Ensures all dependencies used in queryFn are included in queryKey to prevent stale closure issues.
// Rule: @tanstack/query/exhaustive-deps
// Severity: error
// Description: Exhaustive deps rule for useQuery
// Provides auto-fix suggestions for missing dependenciesUsage Example:
// ❌ Bad - missing dependencies in queryKey
useQuery({
queryKey: ['user'],
queryFn: () => fetchUser(userId, settings), // userId and settings not in queryKey
});
// ✅ Good - all dependencies included
useQuery({
queryKey: ['user', userId, settings],
queryFn: () => fetchUser(userId, settings),
});Disallows rest destructuring in queries to prevent excessive re-renders.
// Rule: @tanstack/query/no-rest-destructuring
// Severity: warn
// Description: Disallows rest destructuring in queriesUsage Example:
// ❌ Bad - rest destructuring can cause issues
const { data, ...rest } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
// ✅ Good - explicit destructuring
const { data, isLoading, error } = useQuery({ queryKey: ['user'], queryFn: fetchUser });Makes sure that QueryClient is stable to prevent unnecessary re-renders.
// Rule: @tanstack/query/stable-query-client
// Severity: error
// Description: Makes sure that QueryClient is stableUsage Example:
// ❌ Bad - creating new QueryClient instance on each render
function App() {
return (
<QueryClientProvider client={new QueryClient()}>
<MyComponent />
</QueryClientProvider>
);
}
// ✅ Good - stable QueryClient instance
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<MyComponent />
</QueryClientProvider>
);
}Disallows putting the result of query hooks directly in a React hook dependency array to prevent infinite re-render loops.
// Rule: @tanstack/query/no-unstable-deps
// Severity: error
// Description: Disallow putting the result of query hooks directly in a React hook dependency arrayUsage Example:
// ❌ Bad - using query result directly in dependency array
const { data: user } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
useEffect(() => {
console.log('User changed');
}, [user]); // This causes infinite re-renders
// ✅ Good - use specific properties or data content
useEffect(() => {
console.log('User changed');
}, [user?.id]); // Only re-run when user ID changesEnsures correct order of inference sensitive properties for infinite queries.
// Rule: @tanstack/query/infinite-query-property-order
// Severity: error
// Description: Ensure correct order of inference sensitive properties for infinite queriesUsage Example:
// ✅ Good - properties in correct order
useInfiniteQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
});Ensures queryFn returns a non-undefined value to prevent runtime issues.
// Rule: @tanstack/query/no-void-query-fn
// Severity: error
// Description: Ensures queryFn returns a non-undefined valueUsage Example:
// ❌ Bad - query function returns void
useQuery({
queryKey: ['user'],
queryFn: () => { console.log('fetching'); }, // returns void
});
// ✅ Good - query function returns data
useQuery({
queryKey: ['user'],
queryFn: () => fetchUser(), // returns Promise<User>
});Ensures correct order of inference-sensitive properties in useMutation().
// Rule: @tanstack/query/mutation-property-order
// Severity: error
// Description: Ensure correct order of inference-sensitive properties in useMutation()Usage Example:
// ✅ Good - properties in correct order
useMutation({
mutationFn: updateUser,
onSuccess: (data) => {
queryClient.invalidateQueries({ queryKey: ['user'] });
},
onError: (error) => {
console.error('Update failed:', error);
},
});import type { ESLint, Linter } from 'eslint';
import type { RuleModule } from '@typescript-eslint/utils/ts-eslint';
type RuleKey = keyof typeof rules;
interface Plugin extends Omit<ESLint.Plugin, 'rules'> {
rules: Record<RuleKey, RuleModule<any, any, any>>;
configs: {
recommended: ESLint.ConfigData;
'flat/recommended': Array<Linter.Config>;
};
}
type ExtraRuleDocs = {
recommended: 'strict' | 'error' | 'warn';
}This ESLint plugin works with all TanStack Query framework adapters and automatically detects the correct query hooks from:
@tanstack/react-query@tanstack/vue-query@tanstack/solid-query@tanstack/svelte-query@tanstack/angular-query-experimentalThe plugin uses intelligent import detection and only applies rules to code that uses TanStack Query hooks.
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:
// Supported import patterns
import { useQuery } from '@tanstack/react-query';
import { useQuery } from '@tanstack/vue-query';
import { useQuery } from '@tanstack/solid-query';
import { useQuery } from '@tanstack/svelte-query';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.