How to use Apollo Client in React
Apollo Client is the most powerful GraphQL client for React, providing intelligent caching, optimistic updates, subscriptions, and error handling out of the box.
As the creator of CoreUI with 25 years of web development experience, I use Apollo Client in data-intensive React dashboards where normalized caching significantly reduces redundant API calls and keeps the UI in sync across components.
Beyond useQuery and useMutation, Apollo’s real power lies in its normalized cache — when any component fetches a user, every other component showing that user updates automatically.
Understanding the cache is what separates basic Apollo usage from production-grade Apollo usage.
Set up Apollo Client with authentication and error handling.
// apolloClient.js
import {
ApolloClient,
InMemoryCache,
createHttpLink,
from,
ApolloLink
} from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import { onError } from '@apollo/client/link/error'
const httpLink = createHttpLink({ uri: '/graphql' })
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: localStorage.getItem('token')
? `Bearer ${localStorage.getItem('token')}`
: ''
}
}))
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, extensions }) => {
if (extensions?.code === 'UNAUTHENTICATED') {
localStorage.removeItem('token')
window.location.href = '/login'
}
console.error(`GraphQL Error: ${message}`)
})
}
if (networkError) {
console.error(`Network Error: ${networkError}`)
}
})
export const client = new ApolloClient({
link: from([errorLink, authLink, httpLink]),
cache: new InMemoryCache()
})
from([errorLink, authLink, httpLink]) chains links in order. Error handling runs first, then auth headers are added, then the HTTP request is made. The onError link intercepts GraphQL and network errors globally.
Optimistic Updates
Update the UI immediately before the server responds.
import { useMutation, gql, useApolloClient } from '@apollo/client'
const TOGGLE_LIKE = gql`
mutation ToggleLike($postId: ID!) {
toggleLike(postId: $postId) {
id
liked
likeCount
}
}
`
function LikeButton({ postId, liked, likeCount }) {
const [toggleLike] = useMutation(TOGGLE_LIKE, {
variables: { postId },
optimisticResponse: {
toggleLike: {
__typename: 'Post',
id: postId,
liked: !liked,
likeCount: liked ? likeCount - 1 : likeCount + 1
}
}
})
return (
<button onClick={() => toggleLike()}>
{liked ? '❤️' : '🤍'} {likeCount}
</button>
)
}
optimisticResponse provides an expected result that Apollo writes to the cache immediately. If the mutation succeeds, the real response replaces it. If it fails, Apollo rolls back to the previous state automatically.
Pagination with fetchMore
Load more items on demand.
import { useQuery, gql } from '@apollo/client'
const GET_POSTS = gql`
query GetPosts($offset: Int!, $limit: Int!) {
posts(offset: $offset, limit: $limit) {
id
title
author
}
postCount
}
`
function PostList() {
const { data, loading, fetchMore } = useQuery(GET_POSTS, {
variables: { offset: 0, limit: 10 }
})
function loadMore() {
fetchMore({
variables: { offset: data.posts.length, limit: 10 },
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult) return prev
return {
...prev,
posts: [...prev.posts, ...fetchMoreResult.posts]
}
}
})
}
return (
<div>
{data?.posts.map(p => <div key={p.id}>{p.title}</div>)}
{data?.posts.length < data?.postCount && (
<button onClick={loadMore} disabled={loading}>Load More</button>
)}
</div>
)
}
fetchMore makes an additional query and merges the results using updateQuery. This pattern implements infinite scroll or “load more” pagination without replacing the existing results.
Best Practice Note
This is the advanced Apollo usage pattern referenced in CoreUI React dashboard templates. For the cache to work correctly, every __typename + id combination must be unique across your API — Apollo uses this for normalization. Configure InMemoryCache with typePolicies when your types use a non-id primary key or require custom merge strategies. See how to use GraphQL in React for the basic setup, including the plain fetch alternative.



