📄 tanstack/query/v4/docs/framework/react/community/lukemorales-query-key-factory

File: lukemorales-query-key-factory.md | Updated: 11/15/2025

Source: https://tanstack.com/query/v4/docs/framework/react/community/lukemorales-query-key-factory



TanStack

Query v4v4

Search...

+ K

Auto

Log In

TanStack StartRC

Docs Examples GitHub Contributors

TanStack Router

Docs Examples GitHub Contributors

TanStack Query

Docs Examples GitHub Contributors

TanStack Table

Docs Examples Github Contributors

TanStack Formnew

Docs Examples Github Contributors

TanStack DBbeta

Docs Github Contributors

TanStack Virtual

Docs Examples Github Contributors

TanStack Paceralpha

Docs Examples Github Contributors

TanStack Storealpha

Docs Examples Github Contributors

TanStack Devtoolsalpha

Docs Github Contributors

More Libraries

Maintainers Partners Support Learn StatsBETA Discord Merch Blog GitHub Ethos Brand Guide

Documentation

Framework

React logo

React

Version

v4

Search...

+ K

Menu

Getting Started

Guides & Concepts

Community Resources

API Reference

ESLint

Plugins

Examples

Framework

React logo

React

Version

v4

Menu

Getting Started

Guides & Concepts

Community Resources

API Reference

ESLint

Plugins

Examples

On this page

Query Key Factory

Copy Markdown

Typesafe query key management with auto-completion features. Focus on writing and invalidating queries without the hassle of remembering how you've set up a key for a specific query!

Installation
------------

You can install Query Key Factory via NPM .

bash

npm i @lukemorales/query-key-factory


npm i @lukemorales/query-key-factory

or

bash

pnpm add @lukemorales/query-key-factory


pnpm add @lukemorales/query-key-factory

or

bash

yarn add @lukemorales/query-key-factory


yarn add @lukemorales/query-key-factory

Quick start
-----------

Start by defining the query keys for your app:

### Declare everything in a single file

tsx

import { createQueryKeyStore } from '@lukemorales/query-key-factory'

export const queryKeys = createQueryKeyStore({
  users: null,
  todos: {
    detail: (todoId: string) => [todoId],
    list: (filters: TodoFilters) => ({
      queryKey: [{ filters }],
      queryFn: (ctx) => api.getTodos({ filters, page: ctx.pageParam }),
    }),
  },
})


import { createQueryKeyStore } from '@lukemorales/query-key-factory'

export const queryKeys = createQueryKeyStore({
  users: null,
  todos: {
    detail: (todoId: string) => [todoId],
    list: (filters: TodoFilters) => ({
      queryKey: [{ filters }],
      queryFn: (ctx) => api.getTodos({ filters, page: ctx.pageParam }),
    }),
  },
})

### Fine-grained declaration by features

tsx

import { createQueryKeys, mergeQueryKeys } from '@lukemorales/query-key-factory'

// my-api/users.ts
export const usersKeys = createQueryKeys('users')

// my-api/todos.ts
export const todosKeys = createQueryKeys('todos', {
  detail: (todoId: string) => [todoId],
  list: (filters: TodoFilters) => ({
    queryKey: [{ filters }],
    queryFn: (ctx) => api.getTodos({ filters, page: ctx.pageParam }),
  }),
})

// my-api/index.ts
export const queryKeys = mergeQueryKeys(usersKeys, todosKeys)


import { createQueryKeys, mergeQueryKeys } from '@lukemorales/query-key-factory'

// my-api/users.ts
export const usersKeys = createQueryKeys('users')

// my-api/todos.ts
export const todosKeys = createQueryKeys('todos', {
  detail: (todoId: string) => [todoId],
  list: (filters: TodoFilters) => ({
    queryKey: [{ filters }],
    queryFn: (ctx) => api.getTodos({ filters, page: ctx.pageParam }),
  }),
})

// my-api/index.ts
export const queryKeys = mergeQueryKeys(usersKeys, todosKeys)

Use throughout your codebase as the single source for writing the query keys for your cache management:

tsx

import { queryKeys, completeTodo, fetchSingleTodo } from '../my-api'

export function Todo({ todoId }) {
  const queryClient = useQueryClient()

  const query = useQuery(queryKeys.todos.detail(todoId))

  const mutation = useMutation({
    mutationFn: completeTodo,
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries({ queryKey: queryKeys.todos.list.queryKey })
    },
  })

  return (
    <div>
      <h1>
        {query.data?.title}
      </h1>

      <p>
        {query.data?.description}
      </p>

      <button
        onClick={() => {
          mutation.mutate({ todoId })
        }}
      >
        Complete Todo
      </button>
    </div>
  )
}


import { queryKeys, completeTodo, fetchSingleTodo } from '../my-api'

export function Todo({ todoId }) {
  const queryClient = useQueryClient()

  const query = useQuery(queryKeys.todos.detail(todoId))

  const mutation = useMutation({
    mutationFn: completeTodo,
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries({ queryKey: queryKeys.todos.list.queryKey })
    },
  })

  return (
    <div>
      <h1>
        {query.data?.title}
      </h1>

      <p>
        {query.data?.description}
      </p>

      <button
        onClick={() => {
          mutation.mutate({ todoId })
        }}
      >
        Complete Todo
      </button>
    </div>
  )
}

Check the complete documentation on GitHub .

Edit on GitHub

Batching Requests

React Query Kit

Partners Become a Partner

Code RabbitCode Rabbit CloudflareCloudflare AG GridAG Grid NetlifyNetlify NeonNeon WorkOSWorkOS ClerkClerk ConvexConvex ElectricElectric SentrySentry PrismaPrisma StrapiStrapi UnkeyUnkey

[###### Want to Skip the Docs?

Query.gg - The Official React Query Course
\

“If you’re serious about *really* understanding React Query, there’s no better way than with query.gg”—Tanner Linsley

Learn More](https://query.gg/?s=tanstack)

You are currently reading v4 docs. Redirect to latest version?

Latest Hide

scarf analytics