Docs
Try Apollo Studio

Using the @defer directive in Apollo Client

Receive query response data incrementally


The @defer directive is currently at the General Availability stage in Apollo Client, and is available by installing @apollo/client@latest. If you have feedback on it, please let us know via GitHub issues.

Beginning with version 3.7.0, Apollo Client Web provides preview support for the @defer directive. This directive enables your queries to receive data for specific fields incrementally, instead of receiving all field data at the same time. This is helpful whenever some fields in a query take much longer to resolve than others.

For a query to defer fields successfully, the queried endpoint must also support the @defer directive. Entity-based @defer support is also at the General Availability stage in Apollo Router and is compatible with all federation-compatible subgraph libraries.

Example

Let's say we're building a social media application that can quickly fetch a user's basic profile information, but retrieving that user's friends takes longer.

GraphQL allows us to declare all the fields our UI requires in a single query, but this also means that our query will be as slow as the field that takes the longest to resolve. The @defer directive allows us to mark parts of the query that are not necessary for our app's initial render which will be resolved once it becomes available.

To achieve this, we apply the @defer directive to an in-line fragment that contains all slow-resolving fields related to friend data:

query PersonQuery($personId: ID!) {
person(id: $personId) {
# Basic fields (fast)
id
firstName
lastName
# Friend fields (slower)
... @defer {
friends {
id
}
}
}
}

Using this syntax, if the queried server supports @defer, our client can receive the "Basic fields" in an initial response payload, followed by a supplementary payload containing the "Friend fields".

Let's look at an example in React. Here's we can assume GET_PERSON is the above query, PersonQuery, with a deferred list of friends' ids:

index.js
import { gql, useQuery } from "@apollo/client";
function App() {
const { loading, error, data } = useQuery(GET_PERSON, {
variables: {
id: 1,
},
});
if (loading) return "Loading...";
if (error) return `Error! ${error.message}`;
return (
<>
Welcome, {data.firstName} {data.lastName}!
<details>
<summary>Friends list</summary>
{data.friends ? (
<ul>
{data.friends.map((id) => (
<li>{id}</li>
))}
</ul>
) : null}
</details>
</>
);
}

When our call to the useQuery hook first resolves with an initial payload of data, loading will go from true to false and firstName and lastName will be populated with the values from the server. Our deferred fields will not exist as keys on data yet, so we must add conditional logic that checks for their presence. When subsequent chunks of deferred data arrive, useQuery will re-render (loading remains false between re-renders from deferred multipart responses) and data will include the deferred data as they arrive.

For this reason, @defer can be thought of as a tool to improve initial rendering speeds when some slower data will be displayed below the fold or offscreen. In this case, we're rendering the friends list inside a <details> element which is closed by default, avoiding any layout shift as the friends data arrives.

Using with code generation

If you currently use GraphQL Code Generator for your codegen needs, note that it doesn't yet support the use of the @defer directive in the code output.

The status of this support is tracked in this GitHub issue.

Edit on GitHub
Previous
Fragments
Next
Error handling