Guide

Pagination

Cursor-based pagination for WPGraphQL connections

WPGraphQL uses cursor-based pagination via the Relay connection pattern. When your query includes pageInfo, WPNuxt automatically generates a composable with pagination support — including pageInfo, loadMore(), and full type safety.

Setup

Create a query in extend/queries/ that includes pageInfo alongside nodes:

extend/queries/PaginatedPosts.gql
query PaginatedPosts($first: Int, $after: String, $last: Int, $before: String) {
  posts(first: $first, after: $after, last: $last, before: $before) {
    pageInfo {
      hasNextPage
      hasPreviousPage
      endCursor
      startCursor
    }
    nodes {
      ...Post
    }
  }
}

Run pnpm nuxt prepare to generate the composable. usePaginatedPosts() is now available with pagination support.

Infinite Scroll

Use loadMore() to fetch the next page and append items to the list. Items accumulate automatically:

<script setup lang="ts">
const { data: posts, pageInfo, loadMore, pending } = await usePaginatedPosts({ first: 10 })
</script>

<template>
  <article v-for="post in posts" :key="post.databaseId">
    <h2>{{ post.title }}</h2>
  </article>

  <button
    v-if="pageInfo?.hasNextPage"
    :disabled="pending"
    @click="loadMore"
  >
    Load more
  </button>
</template>

Each call to loadMore() fetches the next page using the current endCursor and appends the results. Call refresh() to reset back to the first page.

Page-based Navigation

For previous/next page navigation, manage the cursor yourself with reactive params:

<script setup lang="ts">
const after = ref<string>()
const before = ref<string>()

const params = computed(() => {
  if (before.value) {
    return { last: 10, before: before.value }
  }
  return { first: 10, after: after.value }
})

const { data: posts, pageInfo, pending } = await usePaginatedPosts(params)

function nextPage() {
  if (pageInfo.value?.hasNextPage && pageInfo.value.endCursor) {
    before.value = undefined
    after.value = pageInfo.value.endCursor
  }
}

function previousPage() {
  if (pageInfo.value?.hasPreviousPage && pageInfo.value.startCursor) {
    after.value = undefined
    before.value = pageInfo.value.startCursor
  }
}
</script>

<template>
  <article v-for="post in posts" :key="post.databaseId">
    <h2>{{ post.title }}</h2>
  </article>

  <div>
    <button v-if="pageInfo?.hasPreviousPage" @click="previousPage">
      ← Previous
    </button>
    <button v-if="pageInfo?.hasNextPage" @click="nextPage">
      Next →
    </button>
  </div>
</template>
When using page-based navigation with reactive params, pass { clientCache: false } as an option to ensure each page fetch hits the server.

How It Works

WPNuxt detects the connection pattern (pageInfo + nodes as sibling fields) during query parsing. When detected, the generated composable uses useWPConnection instead of useWPContent, which:

  1. Extracts nodes as data (the items array)
  2. Extracts pageInfo separately
  3. Provides loadMore() for accumulation
  4. Handles refresh() with accumulation reset

This is fully automatic — just add pageInfo to your query and the composable gains pagination capabilities.

Copyright © 2026