An ESLint plugin for TanStack Query v5+ to enforce structured and reusable query definitions via queryOptions
.
While TanStack Query is incredibly powerful, managing queryKey and queryFn across a large application can become challenging. Keys can become scattered, logic duplicated, and refactoring can be difficult.
TanStack Query v5 introduced the queryOptions helper to solve these problems by encouraging the colocation of query logic. This plugin enforces the best practices around queryOptions to ensure your codebase stays scalable and easy to read.
Key benefits include:
- Centralize your query definitions.
- Define options once and reuse them everywhere.
- Separated options are easier to unit test.
- Keep your data-fetching layer structured and predictable.
# ✨ Auto-detect
npx nypm install -D eslint eslint-plugin-tanstack-query-options
# npm
npm install -D eslint eslint-plugin-tanstack-query-options
# yarn
yarn add -D eslint eslint-plugin-tanstack-query-options
# pnpm
pnpm install -D eslint eslint-plugin-tanstack-query-options
# bun
bun install -D eslint eslint-plugin-tanstack-query-options
# deno
deno install --dev eslint eslint-plugin-tanstack-query-options
Use eslint.config.js
file to configure rules.
See also: https://eslint.org/docs/latest/use/configure/configuration-files-new.
Example eslint.config.js:
import tanstackQueryOptionsPlugin from 'eslint-plugin-tanstack-query-options';
export default [
...tanstackQueryOptionsPlugin.configs.recommended,
{
'tanstack-query-options/no-hardcoded-query-key': 'error',
},
];
This plugin provides configs:
*.configs.recommended
*.configs.all
Use .eslintrc.*
file to configure rules.
See also: https://eslint.org/docs/latest/use/configure.
Example .eslintrc.js:
module.exports = {
extends: [
'plugin:tanstack-query-options/recommended',
],
rules: {
'tanstack-query-options/no-hardcoded-query-key': 'error',
},
};
This plugin provides configs:
plugin:tanstack-query-options/recommended
plugin:tanstack-query-options/all
If you prefer to configure rules individually, you can create your own configuration object.
Example eslint.config.js:
import tanstackQueryOptionsPlugin from 'eslint-plugin-tanstack-query-options';
export default [
{
plugins: {
'tanstack-query-options': tanstackQueryOptionsPlugin,
},
rules: {
'tanstack-query-options/no-loose-query-key': 'error',
'tanstack-query-options/no-hardcoded-query-key': 'warn',
},
},
];
The rules with the following star ⭐ are included in the recommended configs.
Rule ID | Description | |
---|---|---|
tanstack-query-options/no-loose-query-key | ensures query options are defined as stable, reusable variables | ⭐ |
tanstack-query-options/no-inline-query-options | enforces that queryOptions is not called directly inside query hooks |
⭐ |
tanstack-query-options/no-hardcoded-query-key | discourages the use of hardcoded string or number literals in queryKeys |
Enforces that queryOptions
is not called directly inside query hooks.
This rule enforces the practice of defining queryOptions
as standalone variables to improve code reusability.
/* eslint tanstack-query-options/no-loose-query-key: "error" */
import { useQuery } from '@tanstack/react-query';
import { queryOptions } from '@tanstack/react-query';
function Todos() {
const query = useQuery(
queryOptions({
queryKey: ['todos'],
queryFn: fetchTodos,
}),
);
}
/* eslint tanstack-query-options/no-loose-query-key: "error" */
import { useQuery } from '@tanstack/react-query';
import { queryOptions } from '@tanstack/react-query';
const todosOptions = queryOptions({
queryKey: ['todos'],
queryFn: fetchTodos,
});
function Todos() {
const query = useQuery(todosOptions);
}
Discourages the use of hardcoded string or number literals in queryKeys.
Using magic strings for query keys makes them prone to typos and challenging to manage.
This rule encourages the use of centralized key factories for better type safety, discoverability, and refactoring.
import { queryOptions } from '@tanstack/react-query';
const todoOptions = (id) =>
queryOptions({
queryKey: ['todos', 'detail', id],
queryFn: () => fetchTodoById(id),
});
// src/lib/query-keys.js
export const todoQueryKeys = {
list: () => ['todos'],
detail: (id) => ['todos', 'detail', id],
};
import { queryOptions } from '@tanstack/react-query';
import { todoQueryKeys } from '../lib/query-key';
const todoOptions = (id) =>
queryOptions({
queryKey: todoQueryKeys.detail(id),
queryFn: () => fetchTodoById(id),
});
MIT