We now recommend using the client-preset package for a better developer experience and smaller
impact on bundle size.
Get started on our “React/Vue” guide.
TypeScript React-Query
| Package name | Weekly Downloads | Version | License | Updated |
|---|---|---|---|---|
@graphql-codegen/typescript-react-query | Apr 19th, 2026 |
Installation
npm i -D @graphql-codegen/typescript-react-queryUsage Requirements
In order to use this GraphQL Codegen plugin, please make sure that you have GraphQL operations (query / mutation / subscription and fragment) set as documents: … in your codegen.yml.
Without loading your GraphQL operations (query, mutation, subscription and fragment), you won’t see any change in the generated output.
This plugin generates React-Query Hooks with TypeScript typings.
It extends the basic TypeScript plugins: @graphql-codegen/typescript, @graphql-codegen/typescript-operations - and thus shares a similar configuration.
If you are using the
react-querypackage instead of the@tanstack/react-querypackage in your project, please set thelegacyModeoption totrue.
Config API Reference
fetcher
type: HardcodedFetch | object | string
Customize the fetcher you wish to use in the generated file. React-Query is agnostic to the data-fetching layer, so you should provide it, or use a custom one.
The following options are available to use:
- ‘fetch’ - requires you to specify endpoint and headers on each call, and uses
fetchto do the actual http call. { endpoint: string, fetchParams: RequestInit }: hardcode your endpoint and fetch options into the generated output, using the environmentfetchmethod. You can also useprocess.env.MY_VARas endpoint or header value.file#identifier- You can use custom fetcher method that should implement the exportedReactQueryFetcherinterface. Example:./my-fetcher#myCustomFetcher.graphql-request: Will generate each hook withclientargument, where you should pass your ownGraphQLClient(created fromgraphql-request).
exposeDocument
type: boolean
default: false
For each generate query hook adds document field with a
corresponding GraphQL query. Useful for queryClient.fetchQuery.
Usage Examples
queryClient.fetchQuery(
useUserDetailsQuery.getKey(variables),
() => gqlRequest(useUserDetailsQuery.document, variables)
)exposeQueryKeys
type: boolean
default: false
For each generate query hook adds getKey(variables: QueryVariables) function. Useful for cache updates. If addInfiniteQuery is true, it will also add a getKey function to each infinite query.
Usage Examples
const query = useUserDetailsQuery(...)
const key = useUserDetailsQuery.getKey({ id: theUsersId })
// use key in a cache update after a mutationexposeMutationKeys
type: boolean
default: false
For each generate mutation hook adds getKey() function. Useful for call outside of functional component.
Usage Examples
const mutation = useUserDetailsMutation(...)
const key = useUserDetailsMutation.getKey()exposeFetcher
type: boolean
default: false
For each generate query hook adds fetcher field with a corresponding GraphQL query using the fetcher.
It is useful for queryClient.fetchQuery and queryClient.prefetchQuery.
Usage Examples
await queryClient.prefetchQuery(userQuery.getKey(), () => userQuery.fetcher())errorType
type: string
default: unknown
Changes the default “TError” generic type.
addInfiniteQuery
type: boolean
default: false
Adds an Infinite Query along side the standard one
legacyMode
type: boolean
default: true
If false, it will work with @tanstack/react-query, default value is true.
Usage Examples
Note: all generated hooks are just wrappers around react-query original functions. This codegen
plugin just burns the generated TypeScript types into the operation, and provides flexibility to
choose your fetcher.
Using default fetch
By default, this plugin will generate a fetcher based on the environment global fetch
definition.
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: 'fetch'
}
}
}
}
export default configTo use the generated hooks, import it, and then specify the endpoint and optionally fetchParams:
import { useMyQuery } from './generated'
export const MyComponent = () => {
const { status, data, error, isFetching } = useMyQuery({
endpoint: 'http://localhost:3000/graphql',
fetchParams: {
headers: {
'My-Header': 'XYZ'
}
}
})
}Using fetch with Codegen configuration
If you wish to avoid specifying endpoint and fetchParams on each hook usage, you can specify
those in the codegen.yml file:
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: {
endpoint: 'http://localhost:3000/graphql',
fetchParams: {
headers: {
'My-Header': 'SomeValue'
}
}
}
}
}
}
}
export default configAnd if you wish to have more control over the value, or even provide it in runtime, you can use environment variables:
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: {
endpoint: 'process.env.ENDPOINT'
}
}
}
}
}
export default configYou can even use a custom variable from your code, and add custom imports with add plugin:
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: [
{
add: {
content: "import { endpointUrl, fetchParams } from './my-config';"
}
},
'typescript',
'typescript-operations',
'typescript-react-query'
],
config: {
fetcher: {
endpoint: 'endpointUrl',
fetchParams: 'fetchParams'
}
}
}
}
}
export default configThe generated hooks doesn’t require you to specify anything, you can just use it as-is:
import { useMyQuery } from './generated'
export const MyComponent = () => {
const { status, data, error, isFetching } = useMyQuery({})
}Using graphql-request
If you are using graphql-request, you can set fetcher to graphql-request, and then the
generated React Hook will expect you to pass the GraphQLClient instance (created by
graphql-request library).
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: 'graphql-request'
}
}
}
}
export default configAnd the, while using, provide your client instance:
import { useMyQuery } from './generated'
import { client } from './my-graphql-request-client'
export const MyComponent = () => {
const { status, data, error, isFetching } = useMyQuery(client, {})
}Using Custom Fetcher
If you wish to create a custom fetcher, you can provide your own function as a Mapper string
(file#identifier). Codegen will take care of importing it and use it as a fetcher.
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: {
func: './my-file#myFetcher',
isReactHook: false // optional, defaults to false, controls the function's signature. Read below
}
}
}
}
}
export default configAs a shortcut, the fetcher property may also directly contain the function as a mapper string:
# …
config:
fetcher: './my-file#myFetcher' # isReactHook is false here (the default version)Codegen will use myFetcher, and you can just use the hook directly:
import { useMyQuery } from './generated'
export const MyComponent = () => {
const { status, data, error, isFetching } = useMyQuery({})
}Depending on the isReactHook property, your myFetcher should be in the following signature:
isReactHook: falsetype MyFetcher<TData, TVariables> = (operation: string, variables?: TVariables, options?: RequestInit['headers']): (() => Promise<TData>)isReactHook: truetype MyFetcher<TData, TVariables> = (operation: string, options?: RequestInit['headers']): ((variables?: TVariables) => Promise<TData>)
Usage example (isReactHook: false)
export const fetchData = <TData, TVariables>(
query: string,
variables?: TVariables,
options?: RequestInit['headers']
): (() => Promise<TData>) => {
return async () => {
const res = await fetch('https://api.url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...options
},
body: JSON.stringify({
query,
variables
})
})
const json = await res.json()
if (json.errors) {
const { message } = json.errors[0] || {}
throw new Error(message || 'Error…')
}
return json.data
}
}Usage example (isReactHook: true)
export const useFetchData = <TData, TVariables>(
query: string,
options?: RequestInit['headers']
): ((variables?: TVariables) => Promise<TData>) => {
// it is safe to call React Hooks here.
const { url, headers } = React.useContext(FetchParamsContext)
return async (variables?: TVariables) => {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...headers,
...options
},
body: JSON.stringify({
query,
variables
})
})
const json = await res.json()
if (json.errors) {
const { message } = json.errors[0] || {}
throw new Error(message || 'Error…')
}
return json.data
}
}Note: The return value is an async function, with no params, that returns a Promise with the
actual data.
Using Infinite Query
If you wish to use infinite query for pagination or infinite scroll you can with the
addInfiniteQuery config setting. This will however setup an infinite query for every request
whether in reality it can do it or not.
To use this you need to return an object of new queries, and it blends them in to the query.
Usage example (addInfiniteQuery: true)
with the following query:
query AnimalsQuery($catsRange: Int, $catsStarting: Int, $dogsRange: Int, $dogsStarting: Int) {
cats(range: $catsRange, starting: $catsStarting) {
# …
}
dogs(range: $dogsRange, starting: $dogsStarting) {
# …
}
}import { useInfiniteMyQuery } from './generated'
export const MyComponent = () => {
const { status, data, error, isFetching } = useInfiniteAnimalsQuery(
{
catsRange: 5,
catsStarting: 0,
dogsRange: 10,
dogsStarting: 0
},
{
getNextPageParam(lastPage, allPages) {
const totalLocal = (allPages.length ?? 0) * (queryParams.limit ?? 1)
const totalDogs = lastPage.dogs.items?.length ?? 0
if (totalLocal < totalDogs) {
return {
catsStarting: totalLocal * 5,
dogsStarting: totalLocal * 10
}
}
}
}
)
}