---
title: Pagination
description: A guide to pagination in Houdini
---

# Pagination

It's often the case that you want to avoid querying an entire list from your API in order
to minimize the amount of data transfers over the network. To support this, GraphQL APIs will
"paginate" a field, allowing users to query a slice of the list. The strategy used to access
slices of a list fall into two categories. Offset-based pagination relies `offset` and `limit`
arguments and mimics the mechanisms provided by most database engines. Cursor-based pagination
is a bi-directional strategy that relies on `first`/`after` or `last`/`before` arguments and
is designed to handle modern pagination features such a infinite scrolling.

## Paginated Queries

Regardless of the strategy used, houdini follows a simple pattern: mark the field with
`@paginate`, and provide the "page size" via the `first`, `last` or `limit` arguments to the field.
Once you've done that, you can wire up your query however you prefer. For example, an inline query
supporting offset-based pagination would look something like:

```svelte:typescriptToggle=true
<script lang="ts">
	const userList = graphql(`
		query UserList {
			friends(limit: 10) @paginate {
				id
			}
		}
	`)
</script>

<button on:click={userList.loadNextPage}>
	load next
</button>

{$userList.data.friends.map(friend => friend.id).join(',')}
```

and a field that supports cursor-based pagination starting at the end of the list would look something like:

```svelte:typescriptToggle=true
<script lang="ts">
	const userList = graphql(`
		query UserList {
			friends(last: 10) @paginate {
				edges {
					node {
						id
					}
				}
			}
		}
	`)
</script>

<button on:click={userList.loadNextPage}>
	load next
</button>

{$userList.data.friends.map(friend => friend.id).join(',')}
```

If you are paginating a field with a cursor-based strategy (forward or backwards), the current page
info can be looked up with the `pageInfo` field of store's value:

```svelte:typescriptToggle=true
<script lang="ts">
    const userInfo = graphql(`
        query UserList {
            friends(first: 10) @paginate {
                edges {
                    node {
                        id
                    }
                }
            }
        }
    `)
</script>

{#if $userInfo.pageInfo.hasNextPage}
	<button on:click={userInfo.loadNextPage}>
		load more
	</button>
{/if}
```

## Paginated Fragments

`paginatedFragment` works very much in the same way except for a few caveats. Consider the following:

```svelte:typescriptToggle=true
<script lang="ts">
	const userWithFriends = paginatedFragment(
		user,
		graphql(`
			fragment UserWithFriends on User {
				friends(first: 10) @paginate {
					edges {
						node {
							id
						}
					}
				}
			}
		`)
	)
</script>

{#if $userWithFriends.pageInfo.hasNextPage}
	<button on:click={userWithFriends.loadNextPage}>
		load more
	</button>
{/if}
```

In order to look up the next page for the user's friend. We need a way to query the specific user
that this fragment has been spread into. In order to pull this off, houdini will try to use a generic `Node`
interface and corresponding query by default but you can change the behavior [for any type in your schema](/guides/caching-data#custom-ids).

```graphql
interface Node {
	id: ID!
}

type Query {
	node(id: ID!): Node
}
```

In short, this means that, by default, any paginated fragment must be of a type that implements the Node interface or has a custom resolver configured
so it can be looked up in the api. You can read more information about the `Node` interface in
[this section](https://graphql.org/learn/global-object-identification/) of the graphql community website.

Keep in mind, this is only a requirement for paginated fragments. If your application only uses paginated queries,
you do not need to implement the Node interface and resolver.

## Pagination Modes

Houdini supports two different "modes" of pagination: `Infinite` and `SinglePage`.
You can set the default mode in your [houdini config](/api/config#fields) and/or
override it on a per-paginate directive basis by passing the `mode` argument:

- `Infinite` should be result when the results of loading one page should be added to the
existing value (ie, for an infinite scrolling interface).
- `SinglePage` should used in a scenario where you want to display the items, one page at a
time.

## Mutation Operations

A paginated field can be marked as a potential target for a mutation operation by passing
a `name` argument to the `@paginate` directive. For more information, see [this document](/api/mutation#lists).

```typescript
const userWithFriends = paginatedFragment(
	user,
	graphql(`
		fragment UserWithFriends on User {
			friends(first: 10) @paginate(name: "User_Friends") {
				edges {
					node {
						id
					}
				}
			}
		}
	`)
)
```

