@apollo/client
4.3.5 · 19 Oct 2026MIT5.3M / weekRepositoryHomepagenpmAtom feed
3.14.1 is the newest release of 3.x.
In short
25 breaking1 removed1 deprecated31 new9 fixed1 internal
- breaking
Fixes a bug in
BatchHttpLinkthat removed variables from all requests by default. - breaking
Fix type policy inheritance involving fuzzy
possibleTypes - breaking
Fix
includeUnusedVariablesoption not working withBatchHttpLink - breaking
useBackgroundQuerynow uses its own options type calledBackgroundQueryHookOptionsrather than reusingSuspenseQueryHookOptions. - breaking
Incrementally re-render deferred queries after calling
refetchor settingskiptofalseto match the behavior of the initial fetch. Previously, the hook would not re-render until the entire result had finished loading in these cases. - breaking
Silence useLayoutEffect warning when useSuspenseQuery runs on server
- breaking
Fix an issue when using a link that relied on
operation.getContextandoperation.setContextwould error out when it was declared after theremoveTypenameFromVariableslink. - breaking
Use printed query for query deduplication. Cache
printcalls for GraphQL documents to speed up repeated operations.
Release note
Minor Changes
Fetching with Suspense
Add support for React suspense with a new
useSuspenseQueryhook.useSuspenseQueryinitiates a network request and causes the component calling it to suspend while the request is in flight. It can be thought of as a drop-in replacement foruseQuerythat allows you to take advantage of React's concurrent features while fetching during render.Consider a
Dogcomponent that fetches and renders some information about a dog named Mozzarella:View code 🐶
import { Suspense } from "react"; import { gql, TypedDocumentNode, useSuspenseQuery } from "@apollo/client"; interface Data { dog: { id: string; name: string; }; } interface Variables { name: string; } const GET_DOG_QUERY: TypedDocumentNode<Data, Variables> = gql` query GetDog($name: String) { dog(name: $name) { id name } } `; function App() { return ( <Suspense fallback={<div>Loading...</div>}> <Dog name="Mozzarella" /> </Suspense> ); } function Dog({ name }: { name: string }) { const { data } = useSuspenseQuery(GET_DOG_QUERY, { variables: { name }, }); return <>Name: {data.dog.name}</>; }For a detailed explanation of
useSuspenseQuery, see our fetching with Suspense reference.Adds
useBackgroundQueryanduseReadQueryhooksuseBackgroundQueryinitiates a request for data in a parent component and returns aQueryReferencewhich is used to read the data in a child component viauseReadQuery. If the child component attempts to render before the data can be found in the cache, the child component will suspend until the data is available. On cache updates to watched data, the child component callinguseReadQuerywill re-render with new data but the parent component will not re-render (as it would, for example, if it were usinguseQueryto issue the request).Consider an
Appcomponent that fetches a list of breeds in the background while also fetching and rendering some information about an individual dog, Mozzarella:View code 🐶
function App() { const [queryRef] = useBackgroundQuery(GET_BREEDS_QUERY); return ( <Suspense fallback={<div>Loading...</div>}> <Dog name="Mozzarella" queryRef={queryRef} /> </Suspense> ); } function Dog({ name, queryRef, }: { name: string; queryRef: QueryReference<BreedData>; }) { const { data } = useSuspenseQuery(GET_DOG_QUERY, { variables: { name }, }); return ( <> Name: {data.dog.name} <Suspense fallback={<div>Loading breeds...</div>}> <Breeds queryRef={queryRef} /> </Suspense> </> ); } function Breeds({ queryRef }: { queryRef: QueryReference<BreedData> }) { const { data } = useReadQuery(queryRef); return data.breeds.map(({ characteristics }) => characteristics.map((characteristic) => ( <div key={characteristic}>{characteristic}</div> )) ); }For a detailed explanation of
useBackgroundQueryanduseReadQuery, see our fetching with Suspense reference.
Document transforms
Add the ability to specify custom GraphQL document transforms. These transforms are run before reading data from the cache, before local state is resolved, and before the query document is sent through the link chain.
To register a custom document transform, create a transform using the
DocumentTransformclass and pass it to thedocumentTransformoption onApolloClient.import { DocumentTransform } from "@apollo/client"; const documentTransform = new DocumentTransform((document) => { // do something with `document` return transformedDocument; }); const client = new ApolloClient({ documentTransform: documentTransform });For more information on the behavior and API of
DocumentTransform, see its reference page in our documentation.
New removeTypenameFromVariables link
Introduce the new
removeTypenameFromVariableslink. This link will automatically remove__typenamefields fromvariablesfor all operations. This link can be configured to exclude JSON-scalars for scalars that utilize__typename.This change undoes some work from #10724 where
__typenamewas automatically stripped for all operations with no configuration. This was determined to be a breaking change and therefore moved into this link.For a detailed explanation of
removeTypenameFromVariables, see its API reference.
New skipToken sentinel
- deprecated
Adds support for a
skipTokensentinel that can be used asoptionsinuseSuspenseQueryanduseBackgroundQueryto skip execution of a query. This works identically to theskipoption but is more type-safe and as such, becomes the recommended way to skip query execution. As such, theskipoption has been deprecated in favor ofskipToken.We are considering the removal of the
skipoption fromuseSuspenseQueryanduseBackgroundQueryin the next major. We are releasing with it now to make migration fromuseQueryeasier and makeskipTokenmore discoverable.useSuspenseQueryimport { skipToken, useSuspenseQuery } from "@apollo/client"; const id: number | undefined; const { data } = useSuspenseQuery( query, id ? { variables: { id } } : skipToken );useBackgroundQueryimport { skipToken, useBackgroundQuery } from "@apollo/client"; function Parent() { const [queryRef] = useBackgroundQuery( query, id ? { variables: { id } } : skipToken ); return queryRef ? <Child queryRef={queryRef} /> : null; } function Child({ queryRef }: { queryRef: QueryReference<TData> }) { const { data } = useReadQuery(queryRef); }For a detailed explanation of
skipToken, see its API reference.
New error extraction mechanism, smaller bundles
Add a new mechanism for Error Extraction to reduce bundle size by including error message texts on an opt-in basis.
By default, errors will link to an error page with the entire error message.
This replaces "development" and "production" errors and works without
additional bundler configuration.Bundling the text of error messages and development warnings can be enabled as follows:
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev"; if (process.env.NODE_ENV !== "production") { loadErrorMessages(); loadDevMessages(); }For a detailed explanation, see our reference on reducing bundle size.
New @nonreactive directive
Implement a
@nonreactivedirective for selectively skipping reactive comparisons of query result subtrees.The
@nonreactivedirective can be used to mark query fields or fragment spreads and is used to indicate that changes to the data contained within the subtrees marked@nonreactiveshould not trigger re-rendering. This allows parent components to fetch data to be rendered by their children without re-rendering themselves when the data corresponding with fields marked as@nonreactivechange.Consider an
Appcomponent that fetches and renders a list of ski trails:View code 🎿
const TrailFragment = gql` fragment TrailFragment on Trail { name status } `; const ALL_TRAILS = gql` query allTrails { allTrails { id ...TrailFragment @nonreactive } } ${TrailFragment} `; function App() { const { data, loading } = useQuery(ALL_TRAILS); return ( <main> <h2>Ski Trails</h2> <ul> {data?.trails.map((trail) => ( <Trail key={trail.id} id={trail.id} /> ))} </ul> </main> ); }The
Trailcomponent renders a trail's name and status and allows the user to execute a mutation to toggle the status of the trail between"OPEN"and"CLOSED":View code 🎿
const Trail = ({ id }) => { const [updateTrail] = useMutation(UPDATE_TRAIL); const { data } = useFragment({ fragment: TrailFragment, from: { __typename: "Trail", id, }, }); return ( <li key={id}> {data.name} - {data.status} <input checked={data.status === "OPEN" ? true : false} type="checkbox" onChange={(e) => { updateTrail({ variables: { trailId: id, status: e.target.checked ? "OPEN" : "CLOSED", }, }); }} /> </li> ); };Notice that the
Trailcomponent isn't receiving the entiretrailobject via props, only theidwhich is used along with the fragment document to create a live binding for each trail item in the cache. This allows eachTrailcomponent to react to the cache updates for a single trail independently. Updates to a trail'sstatuswill not cause the parentAppcomponent to rerender since the@nonreactivedirective is applied to theTrailFragmentspread, a fragment that includes thestatusfield.For a detailed explanation, see our
@nonreactivereference and @alessbell's post on the Apollo blog about using@nonreactivewithuseFragment.
Abort the AbortController signal more granularly
HttpLink/BatchHttpLink: Abort theAbortControllersignal more granularly.Before this change, when
HttpLink/BatchHttpLinkcreated anAbortControllerinternally, the signal would always be.aborted after the request was completed. This could cause issues with Sentry Session Replay and Next.js App Router Cache invalidations, which just replayed the fetch with the same options - including the cancelledAbortSignal.With this change, the
AbortControllerwill only be.abort()ed by outside events, not as a consequence of the request completing.
useFragment drops its experimental label
Remove experimental labels.
useFragment, introduced in3.7.0asuseFragment_experimental, is no longer an experimental API 🎉 We've removed the_experimentalsuffix from its named export and have made a number of improvements.For a detailed explanation, see our
useFragmentreference and @alessbell's post on the Apollo blog about usinguseFragmentwith@nonreactivefor improved performance when rendering lists.useFragment improvements
More robust types for the
dataproperty onUseFragmentResult. When a partial result is given, the type is now correctly set toPartial<TData>.Adjust the rerender timing of
useQueryto more closely align withuseFragment. This means that cache updates delivered to both hooks should trigger renders at relatively the same time. Previously, theuseFragmentmight rerender much faster leading to some confusion.Remove the deprecated
returnPartialDataoption fromuseFragmenthook.
More Minor Changes
Add generic type parameter for the entity modified in
cache.modify. Improves TypeScript type inference for that type's fields and values of those fields.Example:
cache.modify<Book>({ id: cache.identify(someBook), fields: { title: (title) => { // title has type `string`. // It used to be `any`. }, => { // author has type `Reference | Book["author"]`. // It used to be `any`. }, }, });Use unique opaque types for the
DELETEandINVALIDATEApollo cache modifiers.This increases type safety, since these 2 modifiers no longer have the
anytype. Moreover, it no longer triggers the@typescript-eslint/no-unsafe-return
rule.Avoid calling
useQueryonCompletedfor cache writes- removed
Remove the
query/mutation/subscriptionoption from hooks that already take that value as their first argument. Prevent accidental widening of inferred
TDataandTVariablesgenerics for query hook option argumentsSimplify
__DEV__polyfill to use imports instead of global scopeAdd .js file extensions to imports in src and dist/*/.d.ts
When changing variables back to a previously used set of variables, do not automatically cache the result as part of the query reference. Instead, dispose of the query reference so that the
InMemoryCachecan determine the cached behavior. This means that fetch policies that would guarantee a network request are now honored when switching back to previously used variables.(Batch)HttpLink: Propagate
AbortErrors to the user when a user-providedsignalis passed to the link. Previously, these links would swallow allAbortErrors, potentially causing queries and mutations to never resolve. As a result of this change, users are now expected to handleAbortErrors when passing in a user-providedsignal.Add the ability to allow
@clientfields to be sent to the link chain.Allow
ApolloCacheimplementations to specify default value forassumeImmutableResultsclient option, improving performance for applications currently usingInMemoryCachewithout configuringnew ApolloClient({ assumeImmutableResults: true })Changes how development-only code is bundled in the library to more reliably enable consuming bundlers to reduce production bundle sizes while keeping compatibility with non-node environments.
Patch Changes
Fix an issue where a call to
refetch,fetchMore, or changingskiptofalsethat returned a result deeply equal to data in the cache would get stuck in a pending state and never resolve.Add
SuspenseCacheas a lazy hidden property on ApolloClient.
This means thatSuspenseCacheis now an implementation details of Apollo Client and you no longer need to manually instantiate it and no longer need to pass it intoApolloProvider. Trying to instantiate aSuspenseCacheinstance in your code will now throw an error.Enforce
export typefor all type-level exports.Prevents the DevTool installation warning to be turned into a documentation link.
Add a
suspenseCacheoption touseSuspenseQueryAdd a
queryKeyoption touseSuspenseQuerythat allows the hook to create a unique subscription instance.Automatically strips
__typenamefields fromvariablessent to the server when usingHttpLink,BatchHttpLink, orGraphQLWsLink. This allows GraphQL data returned from a query to be used as an argument to a subsequent GraphQL operation without the need to strip the__typenamein user-space.Use
React.versionas key for shared Contexts.Fix an issue where cache updates would not propagate to
useSuspenseQuerywhile in strict mode.Make private fields
inFlightLinkObservablesandfetchCancelFnsprotected in QueryManager in order to make types available in@apollo/experimental-nextjs-app-supportpackage when extending theApolloClientclass.Ensure Context value stability when rerendering ApolloProvider with the same
clientand/orsuspenseCachepropFixes a bug in
useMutationso thatonErroris called when an error is returned from the request witherrorPolicyset to 'all' .Return
networkStatusin theuseSuspenseQueryresult.Moves
DocumentTransformto theutilitiessub-package to avoid a circular dependency between thecoreandcachesub-packages.Improve
useBackgroundQuerytype interfaceFixes an issue where
useSuspenseQuerywould not respond to cache updates when using a cache-firstfetchPolicyafter the hook was mounted with data already in the cache.Store React.Context instance mapped by React.createContext instance, not React.version.
UsingReact.versioncan cause problems withpreact, as multiple versions ofpreactwill all identify themselves as React17.0.2.Use
import * as Reacteverywhere. This prevents an error when importing@apollo/clientin a React Server component. (see #10974)Fix a bug in
QueryReferencewherethis.resolveorthis.rejectmight be executed even ifundefined.Add support for the
skipoption inuseBackgroundQueryanduseSuspenseQuery. Setting this option totruewill avoid a network request.Fix the compatibility between
useSuspenseQueryand React'suseDeferredValueandstartTransitionAPIs to allow React to show stale UI while the changes to the variable cause the component to suspend.
Breaking change
nextFetchPolicy support has been removed from useSuspenseQuery. If you are using this option, remove it, otherwise it will be ignored.
- breaking
Fixes a bug in
BatchHttpLinkthat removed variables from all requests by default. - breaking
Fix type policy inheritance involving fuzzy
possibleTypes - breaking
Fix
includeUnusedVariablesoption not working withBatchHttpLink - breaking
useBackgroundQuerynow uses its own options type calledBackgroundQueryHookOptionsrather than reusingSuspenseQueryHookOptions. - breaking
Incrementally re-render deferred queries after calling
refetchor settingskiptofalseto match the behavior of the initial fetch. Previously, the hook would not re-render until the entire result had finished loading in these cases. - breaking
Silence useLayoutEffect warning when useSuspenseQuery runs on server
- breaking
Fix an issue when using a link that relied on
operation.getContextandoperation.setContextwould error out when it was declared after theremoveTypenameFromVariableslink. - breaking
Use printed query for query deduplication. Cache
printcalls for GraphQL documents to speed up repeated operations. - breaking
Introduced some helpers for determining the type of operation for a GraphQL query. This imported the
OperationTypeNodefrom graphql-js which is not available in GraphQL 14. To maintain compatibility with graphql-js v14, this has been reverted to use plain strings. - breaking
More robust typings for the
dataproperty returned fromuseSuspenseQuerywhen usingreturnPartialData: trueor anerrorPolicyofallorignore.TDatanow defaults tounknowninstead ofany. - breaking
Always throw network errors in
useSuspenseQueryregardless of the seterrorPolicy. - breaking
Change an import in
useQueryanduseMutationthat added an unnecessary runtime dependency on@apollo/client/core. This drastically reduces the bundle size of each the hooks. - breaking
Ensure
refetch,fetchMore, andsubscribeToMorefunctions returned byuseSuspenseQueryare referentially stable between renders, even asdatais updated. - breaking
Add
@defersupport touseSuspenseQuery. - breaking
Updates dependency versions in
package.jsonby bumping:@wry/contextto^0.7.3@wry/equalityto^0.5.6@wry/trieto^0.4.3optimismto^0.17.4
to 1. fix sourcemap warnings and 2. a Codesandbox sandpack (in-browser) bundler transpilation bug with an upstream optimism workaround.
- breaking
Hide queryRef in a Symbol in
useBackgroundQuerys return value. - breaking
Use
React.usewhere available - breaking
Throw errors in
useSuspenseQueryfor errors returned in incremental chunks whenerrorPolicyisnone. This provides a more consistent behavior of theerrorPolicyin the hook.
Potentially breaking change
Previously, if you issued a query with @defer and relied on errorPolicy: 'none' to set the error property returned from useSuspenseQuery when the error was returned in an incremental chunk, this error is now thrown. Switch the errorPolicy to all to avoid throwing the error and instead return it in the error property.
- breaking
Adds support for
returnPartialDataandrefetchWritePolicyoptions inuseBackgroundQueryhook. - breaking
Fixed the ability to use
refetchandfetchMorewith React'sstartTransition. The hook will now behave correctly by allowing React to avoid showing the Suspense fallback when these functions are wrapped bystartTransition. This change deprecates thesuspensePolicyoption in favor ofstartTransition. - breaking
Restore Apollo Client 3.7
getApolloContextbehaviour - breaking
Slightly decrease bundle size and memory footprint of
SuspenseCacheby changing how cache entries are stored internally. - breaking
useSuspenseQueryanduseBackgroundQuerywill now properly apply changes to its options between renders. - breaking
The "per-React-Version-Singleton" ApolloContext is now stored on
globalThis, notReact.createContext, and throws an error message when accessed from React Server Components. - breaking
Add support for the
subscribeToMoreandclientfields returned in theuseSuspenseQueryresult.