Rendering Long Lists

You’ll often run into the situation where you want to render a long list of data. It’s also common for that list to be much too long to query or even render on the screen. In order to address this, most APIs will accept a set of arguments on the field designed to window the list and leave it up to the client to keep a running total if the situation calls for it.

As GraphQL has matured, these arguments have somewhat standardized and fall roughly into two categories: cursor-based pagination and offset-based pagination. Houdini supports both but since our API relies on cursor-based pagination that’s what we’re going to show here. If you want to read more about pagination, head over to the pagination guide.

Paginated Queries

Interacting with a paginated query is pretty similar to everything we’ve been doing so far. Just decorate the paginated field with the @paginate directive. Go ahead and change the +page.gql file to look like this:

src/routes/[[id]]/+page.gql
query Info($id: Int! = 1) {
    species(id: $id) {
        name
        flavor_text
        favorite
        evolution_chain {
            ...SpeciesPreview
        }
        ...SpriteInfo

        moves(first: 1) @paginate {
            edges {
                node {
                    ...MoveDisplay
                }
            }
        }
    }

    favorites @list(name:"FavoriteSpecies") {
        ...FavoritePreview
    }
}

At the surface, a paginated query store is basically the same thing as a normal query except for a few extra utilities. They’re pretty self-explanatory but just in case there’s any confusion: loadNextPage is an async function that will load the next page and append the result of the field tagged with @paginate to the existing value in our cache and there is a pageInfo value on the store that contains an object with meta data about the current page. For a more in-depth summary of what you can do with @paginate, you can check out the Pagination Guide.

It’s time to add some visuals. Add an import for the MoveDisplay component and copy the following block as the second child in the right panel (between div#species-evolution-chain and the nav):

src/routes/[[id]]/+page.svelte
<MoveDisplay move={$Info.data.species.moves.edges[0].node} />

Houdini supports a few different approachs for pagination but we won’t go too deep just yet. In our situation, since we want to display 1 move at a time, we need to pass SinglePage as the mode for @paginate:

src/routes/[[id]]/+page.gql
query Info($id: Int = 1) {

    # ...

    moves(first: 1) @paginate(mode: SinglePage) {
        edges {
            node {
                ...MoveDisplay
            }
        }
    }

    # ...
}
src/routes/[[id]]/+page.svelte
<script lang="ts">
    import { UpButton, DownButton } from '~/components'
</script>

<!-- Use this instead of the move display we added earlier -->
<div id="move-summary">
    <MoveDisplay move={$Info.data.species.moves.edges[0].node} />
    <div id="move-controls">
        <UpButton
            disabled={!$Info.pageInfo.hasPreviousPage}
            on:click={async () => await Info.loadPreviousPage()}
        />
        <DownButton
            disabled={!$Info.pageInfo.hasNextPage}
            on:click={async () => await Info.loadNextPage()}
        />
    </div>
</div>
src/routes/[[id]]/+page.svelte
<script>
    import { UpButton, DownButton } from '~/components'
</script>

<!-- Use this instead of the move display we added earlier -->
<div id="move-summary">
    <MoveDisplay move={$Info.data.species.moves.edges[0].node} />
    <div id="move-controls">
        <UpButton
            disabled={!$Info.pageInfo.hasPreviousPage}
            on:click={async () => await Info.loadPreviousPage()}
        />
        <DownButton
            disabled={!$Info.pageInfo.hasNextPage}
            on:click={async () => await Info.loadNextPage()}
        />
    </div>
</div>

You can now verify that it all works by opening the network tab and clicking on the up and down arrows. You should only see network requests being sent when loading a move we haven’t seen before.

That’s it!

This is the last topic we wanted to cover as part of the guide! Thank you so much for getting all the way through - we really appreciate the dedication. You can 🪄 share your achievement to help us! If there were any sections that were confusing, or changes you think would be helpful, please open up a discussion on GitHub.

If you want to read more about what Houdini can do, we recommending checking out the Working with GraphQL guide next.