Apollo comes with a simple method
fetchMore() that you can use to implement offset pagination with infinite scroll.
This kind of pagination is very popular in modern applications due to its convenience: Data is presented as an endless
stream, and you can infinitely scroll without reaching the end (in theory). In this tutorial, we explain how such
pagination can be implemented in a React application using Apollo and
So what needs to be done to achieve the desired effect? The next short section summarizes what you need to do.
How to implement offset-based pagination with Apollo and React
First, we'd like to remind what the offset-based pagination is. With this kind of pagination, your client application must provide an offset and limit values when querying the server for new items to indicate what entries must be returned and also their amount. And to implement offset pagination, we use these technologies:
- Apollo Client to use Apollo in React.
react-apolloto use custom React components
apollo-link-state, a data provider and state manager. You can replace it with a GraphQL server in a real-world application.
Using the listed technologies, we can create resolvers, components, and Apollo Client instance and implement pagination.
In this tutorial, we create these components:
- An instance of Apollo Client with configured
- GraphQL resolvers to be passed to
apollo-link-stateto retrieve the data
- A component that renders the data and asks for new entries when a condition is met
- A Higher Order Component, also known as a provider, which sends GraphQL queries for new data
Our example application will retrieve chapters from the famous Harry Potter and Hobbit series of books and render them. This is how the end result looks:
If you want to look at the code with our pagination example, check out these two links:
Let's now focus on the implementation.
Apollo comes with
apollo-link-state, a great package that helps to manage the application state and is used instead of
Redux. Since we don't have a backend API for this applciation, we use
apollo-link-state to get the chapters to render.
App.js with configured Apollo Client and a basic component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
As you can see,
apollo-link-state configurations are set in the
clientState property, which points to the resolvers
to retrieve data upon GraphQL requests, and so
chapterResolvers are imported and used in the Apollo configuration
object. We show the resolvers in the next section.
Concerning the layout, notice that
ApolloProvider component renders the
ChapterListQuery component, which will be
sending GraphQL queries. It also returns a dumb component
ChapterList that renders chapters.
First, we create resolvers, and then we switch to the components that query and render chapters.
Creating resolvers for
apollo-link-state for offset pagination
apollo-link-state, as we've mentioned already, needs a configuration, and the minimal configuration object should have
defaults to return some data by default and
resolvers to return new data.
Have a look at the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
First, we import
mockChapters, an array of chapter objects. Second, we implement the
getChapters() function, which
uses the limit and offset values to slice a chunk of the array and return only the required part.
Pay attention that
apollo-link-state requires that each object has the
__typename property with some value (such as
Chapter) for normalization, which is why each chapter object from
mockChapters is transformed to a new object with
To make sure that our application renders the first ten chapters once you open it in the browser, we set the
defaults.chapters property to a
Finally, in the
resolvers.Query property we set a query
chapters to a
getChapters() call with variables. As
required by the offset-based pagination implementation, you can pass both limit and offset variables into
getChapters(). Although, in this implementation we only pass an offset and use a default limit.
Now we can focus on creating a component that sends a new GraphQL query each time you scroll to the end of the chapter list.
Creating a React component with a GraphQL query
Since it's a bad idea to create a component that both renders data and sends queries to the database, we created
ChapterListQuery, a Higher Order Component that's only concerned with querying the backend for data. The other
ChapterList, will render the chapters. We'll look at it in the next section.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
There are several key aspects that you need to pay attention to:
GET_CHAPTERS query with
Here's the GraphQL query to be send to get new chapters:
1 2 3 4 5 6 7 8
GET_CHAPTERS is a typical query that uses Apollo's
gql method to get new data. We specified these three values in
limit, the number of items that the resolver must send in one go.
offset, a pointer to the item starting from which the resolver will retrieve data. Offset equals the length of the previous data.
@client, a directive needed only for
apollo-link-state. It tells Apollo that the data must be resolved from the cache and making a network request isn't required.
This query will send the limit and offset values to the resolver so that the resolver understands how much data it should return.
Query wrapper component with the
We use a
Query component (kindly presented by
react-apollo) to access data from our component and, importantly, to
fetchMore() method to fetch new data when necessary.
ChapterListQuery returns a new component,
ChapterList, and passes it two props attributes:
1 2 3 4 5 6 7 8 9 10 11
chapters are necessary for is self-evident. But what's important to successfully implement offset-based
pagination is to add an
Let's have a closer look at
1 2 3 4 5 6 7 8 9 10 11 12 13
As you can see,
onLoadMore() calls the Apollo's
fetchMore() method, which gets returned by a GraphQL query and is
available thanks to
fetchMore() receives an object parameter with two properties. First, we must pass the variables that the resolver will
use to slice the next part of the chapters array to return. We pass only an
offset value, which always equals the
length of the current
chapters array, but you can also pass a
limit value if it's necessary for your application.
There's another property inside
updateQuery(). This is a function that accepts two parameters.
One parameter is the data returned by the previous query, and the other parameter is the new data stored in the
updateQuery() is necessary to change the result of the current query on the fly. Consider this: each
GraphQL query will only return a part of the chapters array, whilst you need to display not only the chapters returned
now, but all the chapters including the ones loaded earlier. Hence, you need to merge the current chapter array in
the React application with its next part.
You can look at it as if you first cut the original array
mockChapters in parts and then re-create it chunk by chunk
on the client.
That's it for the
ChapterListQuery component, and we can review the
ChapterList component with infinite scroll
We need a component to display a list of chapters with a scroll bar. This component,
ChapterList, receives chapters
onLoadMore() function for loading more data. And whenever we scroll to the bottom of the list, the
onLoadMore function gets called.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
First, we created a function
handleScroll() that gets called whenever the
scroll event happens and executes the
handleScroll() also calculates whether it's time to load new chapters by simply analyzing if
you scrolled until the last item in the list. And if you did,
onLoadMore() gets called and, provided there are more
chapters, a GraphQL query is called, new chapters are rendered.
The entire flow is this:
ChapterListgets rendered and runs the
onLoadMore()method provided by
ChapterListQuerysends a GraphQL query, which gets resolved by
apollo-link-state(no HTTP request is sent).
apollo-link-stateuses resolvers to slice a chunk fo chapters using the passed offset and limit values and respond to the query
- New chapters are returned, and the
fetchMore()gets called to add the latest chapters to the array of already loaded chapters.
- React re-renders the application by adding new chapters.
That's how you implement offset-based pagination with infinite scroll in React using Apollo and