Skip to main content

queryOptions

The queryOptions helper provides type-safe query options with proper type inference. It’s a lightweight identity function that helps TypeScript infer types correctly and provides better autocomplete.

Import

import { queryOptions } from '@tanstack/react-query'

Signature

function queryOptions<
  TQueryFnData = unknown,
  TError = DefaultError,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey,
>(
  options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>
): UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
  queryKey: DataTag<TQueryKey, TQueryFnData, TError>
}

Type Parameters

TQueryFnData
type
default:"unknown"
The type of data returned by the query function
TError
type
default:"DefaultError"
The type of error that can be thrown by the query function
TData
type
default:"TQueryFnData"
The type of data after transformation by the select function
TQueryKey
type
default:"QueryKey"
The type of the query key

Parameters

options
UseQueryOptions
required
Query configuration options. Accepts all options that useQuery accepts.

Returns

Returns the same options object that was passed in, with enhanced type information for better TypeScript inference.

Examples

Basic Usage

import { queryOptions, useQuery } from '@tanstack/react-query'

const postQueryOptions = queryOptions({
  queryKey: ['post', 1],
  queryFn: () => fetchPost(1),
})

function Post() {
  const { data } = useQuery(postQueryOptions)
  return <div>{data.title}</div>
}

Reusable Query Definitions

import { queryOptions, useQuery } from '@tanstack/react-query'

// Define query options once
const postQueries = {
  all: () => queryOptions({
    queryKey: ['posts'],
    queryFn: fetchPosts,
  }),
  detail: (id: number) => queryOptions({
    queryKey: ['posts', id],
    queryFn: () => fetchPost(id),
  }),
  comments: (id: number) => queryOptions({
    queryKey: ['posts', id, 'comments'],
    queryFn: () => fetchPostComments(id),
  }),
}

// Use in components
function Post({ id }: { id: number }) {
  const { data: post } = useQuery(postQueries.detail(id))
  const { data: comments } = useQuery(postQueries.comments(id))
  
  return <div>{/* render post and comments */}</div>
}

With Type Inference

import { queryOptions, useQuery } from '@tanstack/react-query'

interface Post {
  id: number
  title: string
  body: string
}

const postQueryOptions = queryOptions({
  queryKey: ['post', 1],
  queryFn: async (): Promise<Post> => {
    const res = await fetch('/api/posts/1')
    return res.json()
  },
})

function Post() {
  // TypeScript knows data is of type Post | undefined
  const { data } = useQuery(postQueryOptions)
  return <div>{data?.title}</div>
}

With Select

import { queryOptions, useQuery } from '@tanstack/react-query'

const postTitleOptions = queryOptions({
  queryKey: ['post', 1],
  queryFn: () => fetchPost(1),
  select: (data) => data.title,
})

function PostTitle() {
  // TypeScript knows data is of type string | undefined
  const { data } = useQuery(postTitleOptions)
  return <h1>{data}</h1>
}

With QueryClient Methods

import { queryOptions, useQueryClient } from '@tanstack/react-query'

const postQueryOptions = queryOptions({
  queryKey: ['post', 1],
  queryFn: () => fetchPost(1),
})

function Component() {
  const queryClient = useQueryClient()

  const handleInvalidate = () => {
    // Type-safe query key
    queryClient.invalidateQueries(postQueryOptions)
  }

  const handlePrefetch = () => {
    // Type-safe prefetching
    queryClient.prefetchQuery(postQueryOptions)
  }

  return <div>{/* ... */}</div>
}

Factory Pattern

import { queryOptions } from '@tanstack/react-query'

interface User {
  id: number
  name: string
  email: string
}

const userQueries = {
  all: () => ['users'] as const,
  lists: () => [...userQueries.all(), 'list'] as const,
  list: (filters: string) =>
    queryOptions({
      queryKey: [...userQueries.lists(), filters],
      queryFn: () => fetchUsers(filters),
    }),
  details: () => [...userQueries.all(), 'detail'] as const,
  detail: (id: number) =>
    queryOptions({
      queryKey: [...userQueries.details(), id],
      queryFn: () => fetchUser(id),
    }),
}

function Users() {
  const { data } = useQuery(userQueries.list('active'))
  return <div>{/* ... */}</div>
}

function User({ id }: { id: number }) {
  const { data } = useQuery(userQueries.detail(id))
  return <div>{/* ... */}</div>
}

Notes

  • queryOptions is an identity function that returns the exact object passed to it.
  • Its primary purpose is to improve TypeScript type inference and provide better autocomplete.
  • It’s particularly useful when you want to share query configurations across multiple components or files.
  • The function adds type annotations to the queryKey that can be used for type-safe query invalidation and prefetching.
  • This pattern is recommended for organizing and reusing query configurations in larger applications.

Build docs developers (and LLMs) love