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:
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
):
<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
:
query Info($id: Int = 1) {
# ...
moves(first: 1) @paginate(mode: SinglePage) {
edges {
node {
...MoveDisplay
}
}
}
# ...
}
<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>
<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.