Tutorial: How to implement infinite loading with URQL GraphQL

Jasser Mark Arioste

Jasser Mark Arioste

Tutorial: How to implement infinite loading with URQL GraphQL

So you're tasked with implementing an infinite loading pattern on mobile using a graphql schema. Let me share my knowledge on how to implement infinite loading on URQL on a Next.js Project. In this tutorial, I'll be showing you on how to implement infinite loading on URQL with rick and morty api as our backend endpoint

Infinite loading a is very common pattern in mobile layout since it's optimized for mobile UX as opposed to pagination which is more suited for desktop.  There are many ways to implement infinite loading in URQL however today I'm going to show to you probably the most simple pattern to implement infinite loading pattern using a custom hook. 

How does and infinite loading pattern work? #

For infinite loading, we have to create a custom hook that tracks the following: the page number and the new items from graphql cache. Every time the page changes, we have to update the items displayed.

Step 1: Project Setup #

Please see the tutorial on how to setup URQL with Next.js as we'll continue from that tutorial. It also contains setup for the rick and morty api and the episodes list that we'll be using so don't skip that part! Once you finish the setup, proceed to the next step.

Step 2: Modifying the Episodes Query #

Let's modify the episodes query in episodes.graphql by adding information on the next page


query Episodes($page: Int) {
  episodes(page: $page) {
    results {
    #Add this part
    info {


Run yarn gen to regenerate the graphql types.

Step 3: Creating the Custom Hook #

Lets create the file first:

#create the hooks folder in the root project directory for all our custom hooks
mkdir hooks && cd hooks 
touch useLoadEpisodes.tsx

Insert the code:

import { useEffect, useState } from "react";
import { useEpisodesQuery } from "../graphql/episodes.gql";
import { Episode } from "../graphql/types";

export const useLoadEpisodes = () => {
  const [items, setItems] = useState<Partial<Episode | null>[]>([]);

  const [page, setPage] = useState(1);

  const [{ data, fetching }] = useEpisodesQuery({
    variables: {
      page: page,

  const episodes = data?.episodes?.results ?? [];

  //this is the important part, whenever the episodes changes, we concatenate it to the previous list
  useEffect(() => {
  }, [episodes]);

  const next = data?.episodes?.info?.next;

  //when load more is called, we modify the page so that it triggers another query to the backend.
  const loadMore = () => {
    if (next) {

  //return the important items for our interface
  return {
    hasNext: Boolean(next),

Step 4: Implement infinite loading in pages/index.tsx #

Inside pages/index.tsx, we'll make a couple of changes to take into account infinite loading:


import type { NextPage } from "next";
import { useLoadEpisodes } from "../hooks/useLoadEpisodes";

const Home: NextPage = () => {
  //use the custom hook instead of the generated hook
  const { items, hasNext, loadMore, fetching } = useLoadEpisodes();

  const episodes = items;
  return (
    <div className="container">
      {/* this stays the same */}
      <ol className="episode-list">
        {episodes?.map((episode, i) => {
          return (
            <li key={i} className="episode">
              <h2 className="episode-title">{episode?.name}</h2>
              <p className="episode-ep">{episode?.episode}</p>
              <time dateTime={episode?.air_date ?? ""}>
      {/* add loading indicator for when we are fetching data from the api */}
      {fetching && <p>Loading...</p>}
      {/* add a load more button and call loadMore when clicked */}
      {/* hide it there are no next pages */}
      {!fetching && hasNext && (
        <button className="load-more" onClick={loadMore}>
          Load more

export default Home;

Bonus Step: Style and Refactor your components #

I'll leave this step up to you since it's out of the scope of this tutorial. However, you may check the completed code and the demo below:

Resources #

- Photo by Rafael Cerqueira from Pexels

Share this post!

Related Posts


This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle